home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Atari Mega Archive 1
/
Atari Mega Archive - Volume 1.iso
/
language
/
gfaxpert.lzh
/
GFAXPERT.ASC
< prev
next >
Wrap
Text File
|
1987-04-22
|
225KB
|
5,736 lines
Your Second
GFA-BASIC 3.0
Manual
Second Edition
(c) Han Kempen
Coevorden, 1990
Everything you always wanted to know about
GFA-Basic 3.0, but were afraid to ask
CONTENTS
INTRODUCTION .......................... 7
1. GENERAL
Start-up .............................. 9
Application ........................... 9
Monitor ............................... 10
Break ................................. 10
Operating System ...................... 10
2. THE EDITOR
Abbreviated Commands .................. 12
Syntax ................................ 12
Folded Procedures ..................... 13
Tab ................................... 13
Cut and Paste ......................... 13
Load .................................. 13
Save .................................. 14
Llist ................................. 14
Insert-mode ........................... 15
Direct mode ........................... 15
DEFLIST ............................... 15
Special Characters .................... 16
3. VARIABLES
Variable Type ......................... 17
DEFWRD ................................ 17
Boolean ............................... 17
Integer ............................... 18
Floating Point ........................ 18
VAR ................................... 18
FUNCTION .............................. 19
CLEAR ................................. 19
ERASE ................................. 19
DUMP .................................. 19
TYPE .................................. 20
READ .................................. 20
SWAP .................................. 20
TIME$ ................................. 21
TIMER ................................. 21
DATE$ ................................. 22
4. MEMORY
RAM ................................... 23
INT{} ................................. 23
RESERVE ............................... 23
INLINE ................................ 24
MALLOC ................................ 25
5. SORT
QSORT v. SSORT ........................ 26
QSORT of number-arrays ................ 26
QSORT of string-arrays ................ 26
6. OPERATORS and NUMERICAL FUNCTIONS
\ ..................................... 29
PRED and SUCC ......................... 29
MOD ................................... 29
BCLR .................................. 29
BSET .................................. 30
BCHG .................................. 30
LOG ................................... 30
SINQ and COSQ ......................... 30
EQV ................................... 31
CARD and SWAP ......................... 31
MAX ................................... 31
Correlation ........................... 31
7. STRINGS
INSTR ................................. 32
LSET and RSET ......................... 32
Parser ................................ 32
8. KEYBOARD INPUT
INKEY$ ................................ 33
INPUT ................................. 34
INPUT$ ................................ 35
LINE INPUT ............................ 35
KEYTEST ............................... 35
KEYGET ................................ 35
KEYLOOK ............................... 36
KEYPRESS .............................. 37
KEYDEF ................................ 37
Keyboard .............................. 37
Keyclick, Keyrepeat and CapsLock ...... 38
9. SCREEN OUTPUT
PRINT ................................. 40
LOCATE ................................ 41
PRINT TAB ............................. 42
Setscreen (XBIOS 5) ................... 42
Font .................................. 43
10. PRINTER
Printer ready ......................... 45
HARDCOPY .............................. 45
Printer-commands ...................... 47
11. FILES
Floppy Write Test ..................... 50
Step Rate ............................. 50
RAM-disk .............................. 50
DIR$() ................................ 50
DIR and FILES ......................... 51
FSFIRST and FSNEXT .................... 52
EXIST ................................. 54
LOF ................................... 54
TOUCH ................................. 54
NAME .................................. 54
KILL .................................. 54
File Copy ............................. 55
Disk Format ........................... 55
File Allocation Table (FAT) ........... 58
Sectors ............................... 59
Bootsector ............................ 61
BLOAD ................................. 61
INP and OUT ........................... 62
INPUT and LINE INPUT .................. 62
STORE and RECALL ...................... 62
FILESELECT ............................ 63
12. MIDI
INPMID$ ............................... 67
INP ................................... 67
Midi-commands ......................... 67
13. MODEM
INPAUX$ ............................... 70
INP ................................... 70
Rsconf (XBIOS 15)...................... 70
14. MOUSE
Editor ................................ 71
Fileselector .......................... 71
MOUSE ................................. 71
SETMOUSE .............................. 71
DEFMOUSE .............................. 72
15. JOYSTICK
STRIG and STICK ....................... 74
16. SOUND
SOUND and WAVE ........................ 75
Dosound (XBIOS 32) .................... 75
Samples ............................... 76
Speech ................................ 77
Soundmachine .......................... 77
17. PROGRAM DECISIONS
IF ... ENDIF .......................... 78
SELECT ................................ 78
18. PROGRAM LOOPS
Calculations .......................... 79
FOR ... NEXT .......................... 79
Loops ................................. 80
19. PROGRAM CONTROL
GOSUB ................................. 82
ON BREAK GOSUB ........................ 82
ERROR ................................. 82
EVERY and AFTER ....................... 82
GOTO .................................. 82
DELAY ................................. 83
CHAIN ................................. 83
EXEC .................................. 83
20. GRAPHICS
SETCOLOR and VSETCOLOR ................ 84
Palette ............................... 84
DEFMARK ............................... 87
DEFFILL ............................... 87
DEFLINE ............................... 89
DEFTEXT ............................... 89
GRAPHMODE ............................. 90
PLOT and DRAW ......................... 91
PCIRCLE ............................... 92
CURVE ................................. 92
TEXT .................................. 92
SPRITE ................................ 93
VQT_EXTENT ............................ 95
Line-A ................................ 95
HLINE ................................. 96
ACHAR and ATEXT ....................... 96
GET and PUT ........................... 96
Degas-Pictures ........................ 97
Neochrome-Pictures .................... 98
VSYNC ................................. 98
Scroll ................................ 98
ACLIP ................................. 99
Blitter ............................... 99
21. EVENTS
MENU() ................................ 101
ON MENU BUTTON ........................ 101
ON MENU IBOX .......................... 102
22. PULLDOWN MENU
OPENW 0 ............................... 103
Desk-submenu .......................... 103
File-submenu .......................... 103
23. WINDOWS
GFA-windows ........................... 104
CLOSEW ................................ 104
TITLEW ................................ 105
CLEARW ................................ 105
24. AES-LIBRARY
ALERT ................................. 106
SHEL_GET and SHEL_PUT ................. 106
25. GFAXPERT-FILES
GFAXPERT.DOC .......................... 108
GFAXPERT.LIB .......................... 108
INLINE ................................ 108
STANxxxx.LST .......................... 108
START ................................. 112
small print ........................... 113
EPILOGUE .............................. 114
INDEX ................................. 115
INTRODUCTION
Right now you are reading the second edition of the text GFAXPERT.DOC.
This text is not meant to be a replacement of your GFA-manual. On the
contrary, I assume you are already familiar with the GFA-manual and now
want to know everything about GFA-Basic 3.0 that is not described properly
in the manual.
This text is about the (hidden) power of GFA-Basic 3.0. You will find
nothing about version 3.5, apart from this sentence. And this sentence,
because I would like to mention that I don't understand why GFA launched
version 3.5. GFA-Basic is becoming far too expensive.
All remarks in this text, especially about bugs, are based on GFA-Basic
3.07. I still hope version 3.08 will become available (how naive...),
otherwise I'll have to wait for version 4. Not version 4.00 of course,
because I know there will be at least 200 bugs in the first release.
I wrote this text because I was disappointed by the contents of most books
about GFA-Basic 3.0. Some authors use page after page to describe a boring
program. Others delve deeply into menu's, windows and RSC, but ignore the
"regular" Basic-commands. Instead of complaining, I decided to write the
book that I would really like to see myself. Here it is. Forget all other
books about GFA-Basic, you need only two things: your GFA-manual and this
text. And to be honest, perhaps a good book about the AES-library, because
you won't find much about that subject in this text.
In this text you will find quite a few Procedures. Most Procedures that
are listed (or mentioned) in this text can be found in one of the LST-
files in the folder GFAXPERT.LIB. Downloaders should look for the file
GFAXPRT2.ARC.
I strongly advise you to read the chapter 'GFAXPERT-FILES' thoroughly,
before you try to use the Procedure-library GFAXPERT.LIB. Especially note
the following :
(1) word-variables are the default
(2) a couple of variables are declared as Standard Globals
(3) some Standard Functions are defined
(4) a few Standard Procedures are present
(5) the Standard Array color.index() is declared
A Procedure could use any of the Standard Globals, Functions, Procedures
or the Standard Array. I usually mention this in comment-lines in the
Procedure. If you Merge such a Procedure into an existing program, you
will have to adapt your program or the Procedure.
I still believe in GFA-Basic 3.0. And I also believe in sharing ideas,
Procedures and programs with other users, starting with you. If you feel
guilty about receiving all these goodies for (almost) nothing, you will
find a free consult in the chapter 'EPILOGUE'.
I could only write this text because many GFA-users shared their
experience with others. I dedicate this text to them. Special thanks to
everyone who pointed out bugs, mistakes and omissions in the first edition
of GFAXPERT.DOC. All mistakes in the second edition are made by
Han Kempen
1. GENERAL
Start-up
Programs in an AUTO-folder are executed automatically after a reset. The
interpreter GFABASIC.PRG is a GEM-program, and cannot be started in this
way. With TOS 1.4 you can install a GEM-program as auto-booting. With
older TOS-versions you could use a program like HEADSTRT.PRG in your AUTO-
folder to start GFABASIC.PRG automatically. You can't start a GFA-program
this way (I think).
If you have written a (compiled) program that can be run either from the
AUTO-folder or from the desktop, you can determine which is the case :
IF PEEK(&H2C+4)=0 ! 4th byte of Line-F vector
(...) ! AUTO
ELSE
(...) ! desktop
ENDIF
There are several ways to (re)start your computer. The obvious one is to
switch the ST off, wait a few seconds (15 seconds with a 1040 ST!), and
switch on again. This is called a "cold" or "hard" reset. Your computer
suffers a little, and it takes some time. If you use the reset-button on
your ST, you perform a "warm" or "soft" reset. The operating system
automatically performs a warm reset if you switch between Low and Medium
resolution on the desktop. If you suspect a program of changing system
variables, you should always use a cold reset. After a warm reset the
system variables in low memory are not initialised again. Garbage will
stay there and will undoubtedly lead to interesting effects. You can
perform both a warm and a cold reset from GFA-Basic with the following
Procedures :
PROCEDURE coldstart
SLPOKE &H420,0
SLPOKE &H426,0 ! probably not necessary
SLPOKE &H43A,0
~XBIOS(38,L:LPEEK(4))
RETURN
'
PROCEDURE warmstart
~XBIOS(38,L:LPEEK(4))
RETURN
If you would like to boot from your second (external) drive B, try :
SLPOKE &H446,1 ! boot from drive B after next reset
@warmstart
Application
It is convenient to install the extension GFA as an application for
GFABASIC.PRG. Click once on GFABASIC.PRG and choose Install Application
from the Options-menu. Type GFA as Document Type, click on OK and save the
desktop. If you double-click a GFA-program (extension .GFA) from the
desktop, GFABASIC.PRG is automatically loaded first. Choosing Install
Application you will only see the most recently installed application. Use
a disk-editor to examine the file DESKTOP.INF and you will find all
installed applications (look for #G). You could also use the method
described in the paragraph 'SHEL_GET and SHEL_PUT', or the RECALL-method
from the paragraph 'STORE and RECALL'.
Monitor
The Atari colour monitor SC1224 works with a vertical frequency of either
50 Hz or 60 Hz :
SPOKE &HFF820A,254 ! 50 Hz
SPOKE &HFF820A,252 ! 60 Hz
For 60 Hz, bit 1 of the Sync Mode Register is cleared. Don't change bit 0,
or the video controller chip will not use the so-called sync pulses. After
a reset, the operating system defaults to 50 Hz. The screen is a little
larger than at 60 Hz, but the screen flickers slightly. If you connect
your ST to a TV through a modulator, you should use 50 Hz. Otherwise you
are advised to use 60 Hz.
Break
It's not easy to find in the GFA-manual: you can stop a running program by
pressing <Control> <Left Shift> <Alternate> simultaneously. But if you're
reading this text, you know this already. It's impossible to interrupt a
program during DELAY! Study the Standard Procedure Break in one of the
STANxxxx.LST-files to see how I react after a 'Break'.
Operating System
If you program in a language like GFA-Basic, you won't notice much of the
actual workhorse inside your ST-computer: The Operating System (TOS). But
even GFA-Basic does not have a Basic equivalent for all TOS-functions,
although you can use almost all functions from GFA-Basic.
TOS can be divided in two main parts: (GEM)DOS and GEM. The first is a
collection of "lower level" routines for communication with keyboard,
screen, printer, etc. In GFA-Basic you can call these routines with the
commands BIOS, XBIOS and GEMDOS. The Graphics Environment Manager (GEM)
consists of two collections of routines: the VDI (Virtual Device
Interface) and the AES (Application Environment Services). The VDI takes
care of regular graphics and should have included GDOS. Atari didn't
include GDOS in the VDI, so you have to load it if you need it. Most VDI-
functions have a Basic equivalent in GFA. The AES takes care of the
communication with the user through menu, Alert-box, window, etc. Most
AES-functions can be accessed through the AES-library in GFA-Basic 3.0.
With GEMDOS-function 48 (Sversion) you can find the version of your
GEMDOS. For both the old TOS and the Blitter-TOS &H1300 (version 0.19) is
returned. The French Turbo-DOS has version 0.20 and the new Rainbow TOS of
1988 has version 0.21.
Another way to find out the version of TOS uses the system header of TOS
(not necessarily located in ROM!) :
adr%=LPEEK(&H4F2)
version$=HEX$(DPEEK(adr%+2))
The good old ROM-TOS (1986, actually not so good) has version &H0100
(1.0), the Mega-ST Blitter-TOS (1987) version &H0102 (1.2). And of course
the new TOS ('Rainbow TOS') has version 1.4. You could also examine the
date of your TOS-version :
date$=HEX$(LPEEK(adr%+24))
My ancient TOS 1.0 has '11201985' as the date.
2. THE EDITOR
Abbreviated Commands
The editor recognizes the following abbreviations (not a complete list) :
ALINE - ALI FILESELECT- FILE POLYMARK - POLYM
ARECT - AR FILL - FI PRINT - P or ?
ARRAYFILL - ARR FUNCTION - FU PROCEDURE - PRO
ATEXT - AT GOSUB - G or @ PSET - PS
BMOVE - B GRAPHMODE - GRA QUIT - Q
BOUNDARY - BOU HIDEM - HI REPEAT - REP
CASE - CA HLINE - HL RESTORE - RES
CIRCLE - CI IF - I RETURN - RET
CLOSE - CL INPUT - INP RSET - RS
COLOR - C LINE - LI SELECT - S
DATA - D LINE INPUT- LI SETCOLOR - SET
DEFFILL - DEFF LOCAL - LOC SETMOUSE - SETM
DEFLINE - DE LOOP - L SGET - SG
DEFMARK - DEFMA LPRINT - LPR SHOWM - SH
DEFMOUSE - DEFM LSET - LS SWAP - SW
DEFTEXT - DEFT MID$(..)= - MI ..)= TEXT - T
DELETE - DEL MOUSE - MOU UNTIL - U
DRAW - DR NEXT - N VOID - V
EDIT - ED OPEN - O VSYNC - VS
ELLIPSE - ELL PAUSE - PA WAVE - WA
ELSE - E PBOX - PB WEND - WE
ENDFUNC - ENDF PCIRCLE - PC WHILE - W
ENDIF - EN PELLIPSE - PE
ENDSELECT - ENDS PLOT - PL
ERASE - ERA POLYFILL - POLYF
EXIT IF - EX POLYLINE - POL
If the abbreviated command is followed by anything else, you have to
insert a space (e.g. 'C 1'), except with '@' and '?' :
@proc1
G proc1
'
?"hello"
P "hello"
Syntax
The parser checks for correct syntax after you press <Return>. Many typo-
bugs are prevented this way. The only disadvantage is that the parser
recognizes some variables as commands. It's impossible to use the follo-
wing names as the first word on a line: data_byte|, dirty$, double,
printer$, file%, quit!. The last one is nasty, because the parser changes
the line 'quit!=FALSE' into 'QUIT!=FALSE' without warning for a syntax-
error. If you now run the program you will return to the desktop when QUIT
is encountered. Of course you have not lost your valuable program, because
you always Save before you Run. Do you really? If the parser refuses the
name of a variable, you can use LET (e.g. 'LET quit!=FALSE'). But you will
have to change the name if it is a label (e.g. the label 'data1:' could be
changed into '1data:' or 'd.ata1:').
Folded Procedures
If you press <Control> <Help> on a Procedure-line, all Procedures follo-
wing and including the current one are folded/unfolded. You can unfold all
Procedures at once by putting the cursor on the first Procedure-line and
pressing <Control> <Help>. I suggest you leave all Procedures in the
Procedure-Library GFAXPERT.LIB folded, unless you want to examine a
Procedure. The editor-commands 'Find' and 'Replace' will skip folded
Procedures. Never change the Procedure-line of a folded Procedure, always
unfold it first.
Tab
If you press <Tab>, the cursor jumps to the next tab-position, without
altering the current line. If you use <Left Shift> <Tab>, the line is
filled with spaces from the current cursor-position to the next tab-
position. Pressing <Right Shift> <Tab> erases all consecutive spaces to
the left of the current cursor-postition. If the current cursor-position
happens to be a space, this space and all spaces to the right are erased
as well.
Cut and Paste
If you press <Control> <P>, the current line from the cursor to the end of
the line is cut, and saved in an internal buffer. <Control> <O> inserts
the saved line at the current cursor-position. You can use this method to
"cut and paste" a part of a line. Press <Control> <P>, then <Control> <O>
to restore the original line. Move the cursor to the desired position and
press <Control> <O>. Block-operations are only possible with complete
lines.
Load
You can use Load only with *.GFA-files, not with ASCII-files (such as the
*.LST-files). ST-Basic files are ASCII-files, so you can Merge them.
More recent programs in GFA-Basic (from 3.04) cannot be loaded by earlier
interpreters (up to 3.02). Of course you should have the most current
version (at least version 3.07), but the following method should always
work. Lengthen the program until you can load it successfully :
OPEN "A",#1,file$
PRINT #1,STRING$(1000,0)
CLOSE #1
Save
After 'Save' (*.GFA-file) or 'Save,A' (*.LST-file) with an existing file,
the old file is renamed with a BAK-extension. Nice precaution, but you
should delete those *.BAK-files every now and then.
If you kill and save files regularly, new files will be stored in a
fragmented way. Loading a fragmented file takes more time than loading a
file that occupies consecutive sectors on the disk. You can correct this
as follows. First make a backup-disk (yes, you should already have one).
Copy all files to a RAM-disk by clicking on the drive-icon and dragging it
to the window of the destination drive. You can't use disk-copy (dragging
drive-icon to drive-icon) because the destination-drive is a RAM-disk.
Format the source-disk and copy all files back (this must be a file-copy,
not a disk-copy). Now, all files are saved on consecutive sectors. You
could speed up things a little bit more by copying the most-used files
first. The drive-head now needs less time to reach these files.
Don't try to 'Save,A' an existing file to a full disk. I have tried it
once and lost both the original file (should have become a *.BAK-file) and
of course the saved file. Also, the editor (or TOS) had erased the program
from memory... Thank you.
Llist
There has been some confusion about the following point-commands for the
printer :
.p- - point-commands are not printed
.p+ - point-commands are printed again
.llxx - line-width
.plxx - page-length
.pa - form feed
Sometimes the commands '.cp' and '.nu' are mentioned, but I don't know how
to use these.
You can only use one point-command in one line. I use the following lines
for my printer (96 characters/line in Elite-mode) :
.p-
.n4
.lr3
.ll88
Save this as LLIST.LST and Merge it after the last line of a program
before choosing 'Llist'. The actual listing is then printed with 80
characters/line, preceded by the line-numbers (4 characters + space). If
your program is longer than 10000 lines, you should use '.n5', but in that
case you probably don't have time to read this. A nice touch is the
automatic execution of a form feed after printing the listing.
If the listing contains special characters (ASCII-code < 32 or > 126),
some of those characters might be interpreted as printer-commands. My
printer switches to condensed printing after receiving the Atari-symbol.
Installing the proper printer-driver (e.g. PTEPSON.PRG) will prevent that.
But I never install a printer-driver because there are some serious
disadvantages. Read more about it in the paragraph 'HARDCOPY'.
You can stop the printing process by pressing the 'Break'-combination
<Control> <Left Shift> <Alternate>, unless Break has been disabled with
'ON BREAK CONT'. Your printer will continue printing though, until its
input-buffer is empty, or until you turn the printer off.
Insert-mode
If you return to the editor, you'll always be in Insert-mode, even if you
left the editor in Overwrite-mode. By the way, the editor restores the
original colour-palette, so you are not bothered by palette-changes in a
program. In the old days I was sometimes caught by the dreaded phenomenon
of black characters on a black background, but now you can always read the
listing on your screen.
By the second way, there is only one thing I don't like about the GFA-
editor. It's not easy to merge a Procedure from a LST-file with the
program you're working on. Save the program, clear memory with 'New',
'Merge' the LST-file, mark the Procedure as a block, save the block,
'Load' the original program, find the correct line and 'Merge' the saved
block. A new command NMerge (New+Merge) would make the life of a GFA-
programmer a little easier. A better idea would be the command BMerge
(Block-Merge). Then I could choose this command, clip a Procedure-block,
and the block would be inserted in the listing automatically. Are you
reading this, Frank? If you are, how about a function for changing upper
case into lower case. In the GFA-EDITOR I SOMETIMES (oops) forget I
pressed CapsLock. And how about a function to clear a line from the cursor
to the next space-character. And...
Direct Mode
Enter the Direct Mode by pressing <Esc> or <Shift> <F9> in the editor.
In Direct Mode you can restore the previously used line with <Undo>. With
<Up arrow> and <Down arrow> you can even recall up to 8 of the last used
lines. Press <Insert> to switch between Insert-mode and Overwrite-mode. As
usual <Esc> clears the line, but if you press <Esc> on an empty line, you
will return to the editor immediately. You can also return to the editor
with <Control> <Shift> <Alternate>, even without first clearing the
command-line. You can call Procedures in your program from the Direct Mode
(e.g. '@show'). If you (temporarily) merge some special Procedures with
the program you are developing, you could use the Direct Mode as a Command
Line Interpreter.
DEFLIST
The command 'DEFLIST n' only works in Direct Mode, not in a program. You
can also choose 'Deflist' from the drop-down menu, after clicking on the
Atari-symbol.
Special Characters
You can enter characters with ASCII-code 32-126 directly from the key-
board. These characters are usually called ASCII-characters. The codes
0-31 and 127 are used as control-codes, but additionally special charac-
ters have been assigned to these codes. The characters with code 128-255
are sometimes called "extended ASCII-characters" and are partly identical
to the IBM-characters with the same codes. I loosely use the expression
"ASCII-code" for all codes 0-255.
You can enter characters with code 0-31 or 127-255 by pressing <Alternate>
and then entering the character code. Release the <Alternate>-key and the
character appears on your screen. E.g., you could enter the Escape-
character by holding <Alternate> down and pressing <2> and <7>. This is
much faster than entering CHR$(27), but Llisting a file with Escape-
characters is probably not a good idea (less important, 1st Word Plus
could get confused too). Llisting CHR$(27) doesn't bother your printer at
all. It's not possible to enter CHR$(10) or CHR$(13) with the 'Alternate'-
way. You can enter the first by pressing <Control> <A> and then <1> <0>
<Return>. For code 13 you have to use CHR$(13).
Never use code 4 (EOT, visible as left arrow) in a LST-file. If you Merge
a LST-file, the GFA-editor thinks the end of the file is reached at that
point (&H04) and refuses to load anything following this code!
3. VARIABLES
Variable-type
In GFA-Basic we use the expressions 'Byte', 'Word' and 'Integer' for the
three integer-variables. Because all three are integers, the expression
'Integer' for the 4-byte integer-variable is sligthly confusing. In other
languages this variable is called 'Longword' or simply 'Long'. An address
in RAM should always be a 4-byte integer.
DEFWRD
I prefer to declare word-variables as the default (for variables without
postfix) with: DEFWRD "a-z"
I recommend the use of word-variables (2 bytes) for two reasons. In
calculations, the use of the special integer-operators (ADD, SUB, INC,
etc.) speeds the program up considerably. If you insist on using the
regular operators (+, -, etc.) you should use floating point variables
instead. Using the regular operators, the interpreter has to convert
integer-variables to floating point, does the calculation and converts the
result back to integer again. That's why a#*b# is calculated faster than
a&*b&. But MUL(a&,b&) is much faster than a#*b#. It takes some time to
recognize what an expression like DIV(a,MUL(ADD(a,b),SUB(b,c))) means. I
suggest you use the regular operators if the calculation-time is not
critical. In loops, the gain in calculation time really counts, so you
should use the integer-operators. That way you will learn Polish too. The
second reason for using word-variables is, that in a compiled program
calculations with word-integers are usually the fastest.
IMPORTANT: if a number-variable has no postfix in this text, you should
assume it's a word-variable. I'll use the postfix |, % or # where
appropriate. Please note that the interpreter assumes a number-variable
without postfix is a floating point variable, unless you use DEFWRD "a-z".
Boolean
The following five lines :
IF number>0
test!=TRUE
ELSE
test!=FALSE
ENDIF
can be shortened to just one line :
test!=(number>0)
This works, because the '>'-operator returns TRUE or FALSE (actually -1
or 0).
Another little trick :
IF i=1
n=n*2
ELSE IF i=2
n=n*5
ELSE
n=0
ENDIF
This could be shortened to :
n = n*-2*(i=1) + n*-5*(i=2)
The example is ridiculous, but the principle involved could be useful. The
expressions 'i=1' and 'i=2' are either 0 (FALSE) or -1 (TRUE).
It is not necessary to use something like :
IF flag!=TRUE
(...)
ENDIF
You can simply use :
IF flag!
(...)
ENDIF
Integer
You can't assign 2^31 to a 4-byte integer-variable. Although an integer
contains 32 bits, you can't use bit 31. This bit is a flag for a negative
integer. The largest positive number you can assign to an integer-variable
is therefore 2^31-1 (2147483647). I could have written an analogue
paragraph about the 2-byte word-variables, but I didn't.
Floating Point
The range of floating point variables (postfix #) is :
-1.0E1000 < x# < 1.0E1000
Larger or smaller numbers can be used in calculations, but not printed,
because the exponent may contain not more than 3 digits (1.0E1000 is
displayed as 1.0E;00).
VAR
If you call a Procedure and use VAR (call by reference), all variables
and/or arrays after VAR are called by reference. An example to clarify
this :
@test(10,5,number%,array%())
(...)
PROCEDURE test(a,b,VAR x%,y%())
' now a=10 and b=5 (call by value)
' number% and array%() can now be used as x% and y%()
x%=a+b ! global variable number% is now 15
ARRAYFILL y%(),1 ! all elements of array%() are now 1
RETURN
In prehistoric days you could have used SWAP, but VAR makes life easier :
@test(*a%())
(...)
PROCEDURE test(ptr%)
SWAP *ptr%,x%() ! array a%() temporarily renamed as x%()
(...) ! do something with the array
SWAP *ptr%,x%() ! restore pointer before leaving Procedure
RETURN
FUNCTION
You can only leave a FUNCTION by RETURNing a value or a string. This
value/string is usually assigned to a variable. If the FUNCTION returns a
string, the function-name has to end with '$' :
FUNCTION test
RETURN 126
ENDFUNC
'
FUNCTION test$
RETURN "this is a string"
ENDFUNC
CLEAR
Because CLEAR is automatically executed when you run a program, it's not
necessary to start you program with this command.
ERASE
It's impossible to reDIMension an existing array. You first have to ERASE
the existing array and then you can DIMension a new array. It is not
necessary to test for the existence of an array with DIM?() before you use
ERASE. In other words, you can use ERASE even if the array doesn't exist :
ERASE array$() ! just in case this array already exists
DIM array$(200)
After ERASEing an array, GFA rearranges the remaining arrays. All arrays
that have been DIMensioned after the deleted array are moved in order to
fill the gap of the deleted array. This is important if you use V:array(0)
in your program (read the paragraph 'RESERVE').
DUMP
Examine all variables in your program by typing 'DUMP' in Direct Mode.
Press <CapsLock> to slow down the scrolling-speed, or press the right
<Shift>-key to temporarily stop the scrolling. The Procedure Debug enables
you to use DUMP during debugging. This is the best way to discover those
nasty typo-bugs in a variable-name. You'll probably be surprised to see
the names of deleted variables as well. Also, any variable-name you used
in Direct Mode appears. All these names are SAVEd with the program! Delete
all unwanted names as follows (a RAM-disk would be convenient) :
- Load the file
- Save,A (press <Return> in Fileselector)
- New
- Merge (press <Return> again)
- Save (press <Return> once more)
The file could be much shorter after this operation.
TYPE
The command TYPE does not seem to work properly if you use local
variables. I don't know what's wrong.
READ
As a rule, I always RESTORE the appropriate label before READing DATA-
lines. That way I can use DATA-lines in Procedures :
PROCEDURE read.data
RESTORE these.data
(...) ! READ the DATA here
these.data:
DATA 1,2,3,4
RETURN
SWAP
The 52 cards in bridge (or another card-game) can be represented by a
byte-array. Fill the elements 1-52 of the array with the value 1-52. The
values 1-13 would represent the Club-cards (2,3,4,...,Q,K,A), values
14-26 the Diamonds, values 27-39 the Hearts and values 40-52 the Spades.
Shuffling the cards can now be simulated with the Procedure Shuffle :
DIM deck|(52)
FOR i=1 TO 52 ! ignore index 0
deck|(i)=i
NEXT i
@shuffle(deck|())
(...)
PROCEDURE shuffle(VAR proc|())
LOCAL i,j
FOR i=DIM?(proc|())-1 DOWNTO 2
j=RAND(i)+1
SWAP proc|(j),proc|(i)
NEXT i
RETURN
TIME$
You can use TIME$ to print the current time on the screen. In the
Procedure Time you'll find a way to print the time every second, although
TIME$ is updated every two seconds :
PROCEDURE time
' *** activate with : EVERY 200 GOSUB time
' *** global : TIMER$
LOCAL t$
t$=TIME$
IF t$=timer$
MID$(timer$,8)=SUCC(RIGHT$(timer$))
ELSE
timer$=t$
ENDIF
PRINT AT(1,1);timer$
RETURN
TIMER
If you need a stopwatch, you can use the following Procedures :
PROCEDURE stopwatch
' *** global : STOP.SECONDS# STOP.H STOP.M STOP.S WATCH.ON!
LOCAL s#
IF watch.on!
stop.watch#=TIMER
stop.seconds#=(stop.watch#-start.watch#)/200
stop.h=stop.seconds#/3600
s#=stop.seconds#-stop.h*3600
stop.m=s#/60
stop.s=s#-stop.m*60
watch.on!=FALSE
ELSE
watch.on!=TRUE
start.watch#=TIMER
ENDIF
RETURN
'
PROCEDURE print.stopwatch
IF stop.h>0
PRINT stop.h;" h ";stop.m;" m";
ELSE
IF stop.m>0
PRINT stop.m;" m ";stop.s;" s";
ELSE
IF stop.seconds#>=10
PRINT USING "##.# s",stop.seconds#;
ELSE
PRINT USING "#.## s",stop.seconds#;
ENDIF
ENDIF
ENDIF
RETURN
You start the stopwatch by calling the Procedure Stopwatch. Calling this
Procedure again will stop the stopwatch. Then you can PRINT the elapsed
time at the current cursor-position with the Procedure Print.stopwatch, or
you could use one of the Global Variables from the Procedure Stopwatch to
do something else.
DATE$
Find the day of the week with the Procedure Day.of.week :
PROCEDURE day.of.week(day.date$,VAR day$)
LOCAL day,mp,month,year,m,h,w,week$,n
day=VAL(LEFT$(day.date$,2))
mp=INSTR(day.date$,".")
month=VAL(MID$(day.date$,mp+1,2))
year=VAL(RIGHT$(day.date$,4))
IF month<=2
m=10+month
year=year-1
ELSE
m=month-2
ENDIF
h=year/100
y=year-100*h
w=(TRUNC(2.6*m-0.2)+day+y+TRUNC(y/4)+TRUNC(h/4)-2*h) MOD 7
RESTORE weekdays
FOR n=0 TO w
READ day$
NEXT n
'
weekdays:
DATA Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday
RETURN
This Procedure uses Zeller's Congruence to determine the day of the week.
4. MEMORY
RAM
An overview of the memory of my 1040 ST (start at the bottom) :
address
&H FFFFF top of memory of 1040 ST (1024 K)
&H FFD00 767 unused (?) bytes
XBIOS(2) = &H F8000 screen memory (32000 bytes)
HIMEM = &H F4000 16384 unused bytes (MALLOC(-1))
&H ..... free memory (length FRE(0) bytes)
&H 388EA program + variables (length varies)
&H 10C2E GFA-Basic 3.07 interpreter
BASEPAGE = &H 10B2E Basepage GFA-Basic (256 bytes)
&H A100 start of available RAM
&H 6100 global AES-variables
&H 29B4 global BIOS- and GEMDOS-variables
L~A = &H 293A Line A variables
&H 93A local BIOS-variables + BIOS-stack
&H 400 BIOS system-variables
&H 0 exception vectors
The BIOS system-variables (&H400 - &H4FF) are "cast in concrete" by Atari.
Other (undocumented) variables in RAM should be avoided.
INT{}
You can use either INT{adr%} or WORD{adr%}. As you know, w=WORD{adr%} is
faster than w=DPEEK(adr%), but you can't use WORD{} and the other related
commands in supervisor mode. This means you can't access memory below
address &H800. Of course you can PEEK/DPEEK/LPEEK everywhere (DPEEK and
LPEEK on even addresses only), and you can use SPOKE/SDPOKE/SLPOKE to
write in supervisor mode.
RESERVE
The command RESERVE can be used in three different ways :
RESERVE n% : reserve n% bytes for GFA-Basic, release RAM up to HIMEM
RESERVE -n% : reserve last n% bytes of RAM up to HIMEM
RESERVE : restore to original
The second command is my favourite. You must use a multiple of 256 with
RESERVE. After 'RESERVE -400', only 256 bytes are released to GEMDOS. In
this case you would have to use 'RESERVE -512', although you need only 400
bytes! Use the Function Multiple if arithmetic is not your strongest
point :
RESERVE -@multiple(n%,256)
Use 'RESERVE -n%' only once in your program. If you RESERVE memory a
second time with 'RESERVE -m%', GFA releases m% bytes, not n%+m%.
The command RESERVE (restore) does not always function properly. Especial-
ly after EXEC 3 it's often impossible to restore the memory. I suspect
this has something to do with the use of the malloc-function by the
operating system. You could try the following :
RESERVE -n% ! reserve as much as needed (multiple of 256)
base%=EXEC(3,...) ! load, but don't start
(...)
~MFREE(HIMEM) ! memory above GFA-Basic
~MFREE(base%) ! memory from Basepage of loaded program
RESERVE ! hope it works now
Don't be surprised by hang-ups or bombs after this operation...
There are several ways to reserve a part of memory for special purposes
such as music, pictures or other programs. E.g., you could use a string-
variable to store a complete screen or a GET-picture. Don't use strings if
garbage collection is a serious risk. If a large array is declared, the
interpreter sometimes moves the strings in memory to create space for the
new array. If you assign a new string to an existing string-variable, the
old string is not erased. During garbage collection all unused strings are
deleted and the active strings are rearranged. This means the address of a
string (accessed through VARPTR or V:) is not fixed. That's not important
for a GET-picture, because 'PUT x,y,pic$' still works all right. But if
you swap screens with ~XBIOS(5,L:adr%,-1,-1), or call a routine with
~C:adr%(...), garbage collection will be fatal. One solution is not to use
a variable for the address, but to use VARPTR or V:. A different approach
would be the use of a byte-array :
OPEN "I",#1,file$
LET bytes%=LOF(#1) ! how much space is needed?
CLOSE #1
DIM assembler|(bytes%-1) ! create space for assembler-routine
adr%=V:assembler|(0)
BLOAD file$,adr% ! load the routine
You can use the variable adr% safely, because a garbage collection has no
influence on arrays. However, after ERASEing an array, all arrays that
have been DIMensioned after the deleted array are moved in memory. So the
array-method is not reliable either, unless you are certain that ERASE
will not be used after you have determined the address of the byte-array.
Another solution would be to declare the byte-array early in your program,
so it will not be moved after ERASE. The only complete safe way is the use
of INLINE or MALLOC.
INLINE
Use D (=DUMP) to dump an Inline-file in Hex-code to the printer.
Don't use 'Save,A', because you will lose the Inline-code. You can't use
INLINE in LST-files. If you would like to use INLINE in combination with a
LST-file you could proceed as follows. First, create an INLINE-folder in
the main directory and SAVE the Inline-code as a file (extension INL) in
this folder. Merge the LST-file in your program and load the Inline-code
in the INLINE-line. The program should be saved with 'Save'.
The only limitation with INLINE is the maximum length of 32746 bytes. If
you need more space, you have to use MALLOC.
If you change the length of an existing INLINE-line, the editor sometimes
erases a few lines from your program. It's safer to completely delete the
old INLINE-line and then enter the new INLINE-line.
MALLOC
This is how you could use MALLOC :
unused%=MALLOC(-1) ! "unused" memory above HIMEM (used by GEM)
RESERVE -n% ! release last n% bytes to GEMDOS
max%=MALLOC(-1)
IF max%<>0
adr%=MALLOC(max%-unused%) ! n% bytes now available to GFA-Basic
ELSE
' error
ENDIF
(...) ! do something interesting
~MFREE(adr%) ! give back to GEMDOS...
RESERVE ! and back to GFA-Basic
Atari recommends you leave at least 8K to GEM, so you can't go wrong if
you leave 16384 bytes to GEM (see paragraph 'RAM').
Do not use MALLOC to allocate a lot of small memory-blocks, as GEMDOS will
get confused. This problem is related to the "40-folder limit", because
MALLOC uses the same buffer that is used to store information about a
folder (see paragraph 'FILESELECT'). Allocate one large area and split it
up in as many parts as you need. Also consider the use of INLINE, strings
or byte-arrays instead of using MALLOC (read the paragraph 'RESERVE'). Do
not use MALLOC in an accessory, or the allocated memory may be lost if the
user changes the resolution.
MALLOC(-1) returns the size of the largest available memory-block. Expect
problems if another program has allocated a couple of separate memory-
blocks to GEMDOS. In that case you would have to use MALLOC(-1) again,
until a returned value of '0' indicates there are no more memory-blocks
available.
5. SORT
QSORT v. SSORT
QSORT is faster than SSORT, unless the array is almost sorted already. I
understand QSORT uses the recursive Quick-sort method, so you need more
memory than with SSORT (iterative Shell-sort). Sort-freaks can study the
Procedures Quick.sort.int and Shell.sort.int to see how the Quick-sort and
Shell-sort can be implemented with ordinary GFA-Basic commands (like that
dance: Quick, Shell, slow).
QSORT of number-arrays
Number-arrays can be QSORTed in three different ways :
QSORT x%() - sorts array ; also with x|(), x&() or x#()
QSORT x%(),n - sorts elements 0 through n-1
QSORT x%(),n,y%() - elements of y%() are swapped too
In all cases x%(-1) results in sorting in decreasing order.
The third method is interesting if you need a sorted output, but don't
want to change the original array. You can accomplish this as follows :
- copy x%() to temporary array temp%()
- determine index of last element : last=DIM?(temp%())-1
- create index array : DIM index%(last)
- fill index-array with index 0 through last
- sort array : QSORT temp%(),last+1,index%()
- ERASE temp%()
The index-array must be an integer-array (postfix %). Also, the number of
elements is not optional if you sort with an index-array. You can now
print the numbers in array x%() in increasing order :
FOR i=0 TO last
PRINT x%(index%(i))
NEXT i
I have never seen the correct syntax for QSORT in print, so here it is :
QSORT x([s])[,n[,y%()]]
QSORT of string-arrays
A string-array can be sorted in the same three ways as described for
number-arrays. If you have created an index-array with last_name$(), you
could print an alphabetical list :
FOR i=0 TO last
PRINT first_name$(index%(i))'last_name$(index%(i))
NEXT i
There are two extra possibilities with string-arrays.
For the first possibility, create a byte-array and fill with appropriate
ASCII-values :
DIM ascii|(255)
ARRAYFILL ascii|(),32 ! CHR$(32) is the space-character
FOR i=48 TO 57
ascii|(i)=i ! 0 - 9
NEXT i
FOR i=65 TO 90
ascii|(i)=i ! A - Z
NEXT i
FOR i=97 TO 122
ascii|(i)=SUB(i,32) ! a - z converted to A - Z
NEXT i
All characters that are not numbers or letters will be treated as a space
(ASCII-value 32). Use the array ascii|() for sorting a string-array :
QSORT x$() WITH ascii|()
Now 'Atari' and 'ATARI' are treated exactly the same, because the
interpreter uses the array ascii|() to convert lower case letters tempora-
rily to upper case before sorting. You can even treat the international
characters (ASCII-values > 127) correctly, e.g. ascii|(129)=85. Use a few
DATA-lines to add the correct ASCII-value, like this :
DATA 128,67,129,85,130,69
You can combine this method with an index-array as well. The correct
syntax is :
QSORT x$([s]) WITH b|() [,n[y%()]]
If you have done your homework, you should now be able to write a clever
alphabetical sorting-program yourself. You wanted to become an expert,
didn't you? Compare your program with the Procedures Ascii.qsort and
String.index.qsort :
PROCEDURE ascii.qsort(VAR txt$())
IF DIM?(ascii|())=0
@initio.ascii.array
ENDIF
QSORT txt$() WITH ascii|()
RETURN
'
PROCEDURE initio.ascii.array
LOCAL i,code1,code2
DIM ascii|(255)
ARRAYFILL ascii(),32 ! fill with space-character
FOR i=48 TO 57
ascii|(i)=i ! 0 - 9
NEXT i
FOR i=65 TO 90
ascii|(i)=i ! A - Z
NEXT i
FOR i=97 TO 122
ascii|(i)=SUB(i,32) ! a - z, converted to A - Z
NEXT i
RESTORE ascii.data
REPEAT
READ code1,code2
ascii|(code1)=code2
UNTIL code1=0
'
ascii.data:
' *** format : ASCII-code,replacement
DATA 128,67,129,85,130,69,131,65,132,65,133,65,134,65,135,67
DATA 136,69,137,69,138,69,139,73,140,73,141,73,142,65,143,65
DATA 144,69,145,65,146,65,147,79,148,79,149,79,150,85,151,85
DATA 152,121,153,79,154,85,155,67,158,83,160,65,161,73,162,79
DATA 163,85,164,78,165,78,166,65,167,79,176,65,177,79,178,79
DATA 179,79,180,79,181,79,182,65,183,65,184,79,192,121,193,121
DATA 225,83,0,0
RETURN
'
PROCEDURE string.index.qsort(switch!,VAR txt$(),index%())
' *** the index-array has to exist already
' *** if switch!=TRUE, array ascii|() is used
LOCAL last,i
last=DIM?(txt$())-1 ! index of last element
DIM temp$(last)
FOR i=0 TO last
temp$(i)=txt$(i)
NEXT i
FOR i=0 TO last
index%(i)=i
NEXT i
IF switch!
IF DIM?(ascii|())=0
@initio.ascii.array
ENDIF
QSORT temp$() WITH ascii|(),last+1,index%()
ELSE
QSORT temp$(),last+1,index%()
ENDIF
ERASE temp$()
RETURN
The second extra possibility with string-arrays is the use of 'OFFSET o'
to ignore the first 'o' characters during sorting. Suppose you have
created an array files$() with FILES and would like to sort the files by
length :
QSORT files$() OFFSET 13,n
The first character (either a space or '*') and the filename (the next 12
characters) are now ignored during sorting.
6. OPERATORS and NUMERICAL FUNCTIONS
\
The operator '\' (backslash) is identical to DIV :
a=b\c
Integer-division is not as useful as the other integer-operations, because
the result is an integer. Of course it is, but this can easily result in
large rounding errors.
PRED and SUCC
PRED(i) is faster than i-1 and SUCC(i) is faster than i+1. Both functions
can also be used with strings. Note the clever use (ahem) of SUCC in the
following line from the Procedure Time :
MID$(timer$,8)=SUCC(RIGHT$(timer$))
The rightmost digit of timer$ (always even) is increased with one.
MOD
If you use a counter in a loop in order to do something every time the
counter reaches a multiple of 10, you could do this as follows :
IF counter MOD 10=0 ! multiple of 10?
(...) ! yes, do something
ENDIF
You could also use :
IF MOD(counter,10)=0
(...)
ENDIF
BCLR
In GFA-Basic version 2.x you used AND to clear a bit :
x|=x| AND &X11111011 ! clear bit 2 of this byte
Remember, the first (rightmost) bit is bit 0, so a byte contains bit 0-7
from right to left. In GFA-Basic 3.0 you clear a bit simply with :
x|=BCLR(x|,2) ! clear bit 2 of this byte
But if you want to clear more than one bit, you should use the AND-method:
x|=x| AND &X11110000 ! clear bit 0-3 of this byte
In this case you use a so-called mask to clear certain bits. You can use
AND also as a function :
y|=AND(x|,&X11110000) ! even faster than previous example
This way you can test if a bit-mask "fits" a variable :
IF AND(x|,mask|)
(...)
ENDIF
An example of this method :
IF AND(BIOS(11,-1),&X1001)
(...) ! user pressed <Alternate> + right <Shift>
ENDIF
BSET
In GFA-Basic 2.x you needed OR to set a bit, but now you use BSET :
x|=BSET(x|,1)
You still need the OR-method if you want to set more than one bit fast :
x|=x| OR &X1010 ! set bit 1 and 3
The mask &X1010 is used to set bits 1 and 3. Compare this with the AND-
method, where 0 is used to clear a bit, while here 1 is used to set a bit.
You can use OR not only as an operator, but also as a function :
y|=OR(x|,&X1010)
BCHG
Instead of BCHG you should use the XOR-method if you want to change more
than one bit (with a mask) :
x|=x| XOR &X1110 ! change bit 1-3
XOR can be used as a function as well.
LOG
Logarithms with a base other than 10 or e are computed as follows :
LOG(x)/LOG(base)
SINQ and COSQ
If you are going to convert SIN (or COS) into SINQ (COSQ), you'll have to
change from radials to degrees. For example, you would convert SIN(x) to
SINQ(DEG(x)). Although SINQ is about five times faster than SIN, this
extra computation makes the difference a little less spectacular. If you
are lucky, the variable already is in degrees, so you can use SINQ
immediately. SINQ and COSQ should not be used if you need very accurate
results, only if you plot the result and are not going to use it for
further computations. Examine the difference between the use of SIN/COS
and SINQ/COSQ to see if the less accurate result is acceptable. In High
resolution try something like this :
fac#2*PI/639
DRAW 0,200
FOR x=0 TO 639
y#=SIN(x*fac#)
DRAW TO x,200+y#*200
NEXT x
~INP(2)
CLS
'
DRAW 0,200
FOR x=0 TO 639
y#=SINQ(DEG(x*fac#))
DRAW TO x,200+y#*200
NEXT x
~INP(2)
CLS
'
fac#=360/639
DRAW 0,200
FOR x=0 TO 639
y#=SINQ(x*fac#)
DRAW TO x,200+y#*200
NEXT x
~INP(2)
EQV
EQV doesn't work properly. EQV(0,-1) should be 0, but equals -65536. The
bits 16-31 are always set: EQV seems to look at the bits 0-15 only.
CARD and SWAP
If a 4-byte integer (postfix %) consists of two words, you can extract
these with :
low.word=CARD(x%)
high.word=CARD(SWAP(x%))
Both words are interpreted as positive numbers.
Unfortunately, there is no analogue function to swap the low and high
byte of a word. That would be useful if you want to convert a word
into/from Intel-format (MS-DOS). Rotating 8 bits should do the trick :
FUNCTION intel.word(x%)
RETURN CARD(ROR&(x%,8)) ! swap low/high byte
ENDFUNC
Now you should be able to extract the low and high byte of a word :
low.byte|=BYTE(x)
high.byte|=BYTE(@intel.word(x))
MAX
This is how you could find the highest value in a word-array :
PROCEDURE max.array(VAR proc(),high)
' *** return highest value of array proc()
LOCAL last,n
last=DIM?(proc())-1
high=proc(1) ! proc(0) ignored
FOR n=2 TO last
high=MAX(high,proc(n))
NEXT n
RETURN
I often store special information in the element with index 0.
Correlation
Use the Procedure Correlate to determine the correlation between two
arrays.
7. STRINGS
INSTR
The command INSTR(a$,b$,i) always returns '1' if a$=b$. For i=1 that's
fine, but not for i>1. Don't blame me, I'm just the messenger.
LSET and RSET
If you use LSET or RSET, the string is padded with spaces :
a$=" 345"
LSET a$="12"
PRINT a$ ! the interpreter prints "12 ", not "12345"
You can use RSET for right justification of columns :
f$=SPACE$(10)
FOR i=1 TO 10
RSET f$=text$(i)
PRINT f$
NEXT i
Parser
Parsing a string, you will frequently have to split the string into the
target$, the string before and the string after that target$. The
following functions could be helpful :
FUNCTION before$(source$,target$)
' *** returns part of source$ before target$
' *** returns complete source$ if no target$ found
LOCAL p
p=INSTR(source$,target$)
IF p=0
RETURN source$
ELSE
RETURN LEFT$(source$,p-1)
ENDIF
ENDFUNC
'
FUNCTION after$(source$,target$)
' *** returns part of source$ after target$
' *** returns nullstring if no target$ found
LOCAL p
p=INSTR(source$,target$)
IF p=0
RETURN ""
ELSE
RETURN MID$(source$,p+LEN(target$))
ENDIF
ENDFUNC
8. KEYBOARD INPUT
INKEY$
All keypresses are saved in the keyboard-buffer. If you don't want INKEY$
to read an "old" key, you should first clear the buffer :
REPEAT
UNTIL INKEY$="" ! clear keyboard-buffer
key$=""
REPEAT
key$=INKEY$ ! wait for new key
UNTIL key$<>""
In the following table you'll find a few useful decimal ASCII-codes you
can use after 'key$=INKEY$'. In the third column the hexadecimal scan-code
of the key is also mentioned (see paragraph 'KEYGET').
key ASC(key$) scancode
<Backspace> 8 &H0E
<Tab> 9 &H0F
<Return> and <Enter> 13 &H1C and &H72
<Esc> 27 &H01
<Delete> 127 &H53
key ASC(RIGHT$(key$))
<F1> 59 &H3B
<F10> 68 &H44
<Shift> <F1> 84 &H54
<Shift> <F10> 93 &H5D
<Help> 98 &H62
<Undo> 97 &H61
<Insert> 82 &H52
<ClrHome> 71 &H47
<Left arrow> 75 &H4B
<Right arrow> 77 &H4D
<Up arrow> 72 &H48
<Down arrow> 80 &H50
Keys in the second part of this table return a 2-byte value after INKEY$.
You only need the low byte, the high byte is &H00. That's why a Standard
Global like help$ is defined as 'CHR$(0)+CHR$(98)'. Then it's easy to
check if <Help> was pressed :
IF key$=help$
(...) ! <Help>
ENDIF
Otherwise, you have to test as follows :
IF ASC(RIGHT$(key$))=98
(...) ! <Help>
ENDIF
However, read the paragraph 'KEYGET' for the ultimate keypress-processor.
If you are just waiting for any keypress, you could use either of the
following methods (clear the keyboard-buffer first) :
~INP(2) ! my favourite
'
KEYGET code% ! perhaps this is clearer in a listing
'
REPEAT ! a loop is also possible
UNTIL LEN(INKEY$)
The latter method is needed if you are waiting for any keypress or any
mouse-click :
REPEAT
UNTIL LEN(INKEY$) OR MOUSEK
INPUT
If you don't want the question mark to appear after INPUT, use :
LOCATE col,lin
INPUT "",txt$
The nullstring and the comma are essential. Most of the time you'll need
something like :
LOCATE col,lin
INPUT "Enter your name : ",name$
But if the instruction-line is not the same as the INPUT-line, use :
PRINT AT(col1,lin1);"Enter your name :"
LOCATE col2,lin2
INPUT "",name$
After INPUT, the user can press <Insert> to switch between Insert-mode and
Overwrite-mode. I have not been able to discover how to input character-
codes 0-31 after INPUT. It's not important anyway, but I read somewhere
you could use <Alternate> for this purpose. The Alternate-method can be
used for character-codes 128-255 (not 127). In the following table you'll
find some important characters with the decimal ASCII-code :
character ASCII-code character ASCII-code character ASCII-code
á - 160 é - 130 í - 161
à - 133 è - 138 ì - 141
ä - 132 ë - 137 ï - 139
â - 131 ê - 136 î - 140
ó - 162 ú - 163 ÿ - 152
ò - 149 ù - 151 ß - 158
Ö - 148 ü - 129
ô - 147 û - 150
α - 224 ≡ - 240 ½ - 171
β - 225 ± - 241 ¼ - 172
δ - 235 ≥ - 242 ² - 253
µ - 230 ≤ - 243
π - 227 ÷ - 246
τ - 231 ≈ - 247
¢ - 155 © - 189
£ - 156 § - 221
ƒ - 159
If you want to use one of these characters after INPUT, you should hold
<Alternate> down, enter the code, and release <Alternate>. I hope your
printer-driver could digest this table...
You can use <Backspace>, <Delete>, <Left arrow> and <Right arrow> on an
INPUT-line, but also :
<Up arrow> - cursor to start of input-line
<Down arrow> - cursor to end of line
<Esc> - erase line
The first two feel slightly unnatural, I would prefer it the other way: up
to end, down to start.
Both INPUT and LINE INPUT use a special cursor, so it doesn't make much
sense to use XBIOS 21 (Curscon) to do something interesting with the TOS-
cursor.
INPUT$
For the input of a secret password, you could use something like :
PRINT "type password (5 characters) : ";
code$=INPUT$(5)
The password does not appear on the screen.
LINE INPUT
LINE INPUT uses the underscore (_) as the cursor in a window. After you
press <Return>, the underscore is not completely erased. The rightmost
pixel remains visible. I think this is a GEM-bug.
KEYTEST
The KEYTEST-function does not respond to keys such as <Help>, <Undo>, etc.
KEYGET
KEYGET waits for a keypress, just like INP(2). But KEYGET is far more
flexible, because it returns the ASCII-code and the scan-code of any key
and also the state of the special keys <Shift>, <Control>, <Alternate> and
<CapsLock>. Consult your manual for tables of ASCII-codes and scan-codes
(in the paragraph 'INKEY$' you already encountered some important codes).
Study the following example to get an impression of the easy way you can
examine all keypresses with KEYGET :
@initio_keyget ! assign variables to the three codes
'
DO
KEYGET get_code%
@keyget ! process keypress there (not included)
LOOP
'
PROCEDURE initio_keyget
ABSOLUTE ascii|,V:get_code%+3
ABSOLUTE scan|,V:get_code%+1
ABSOLUTE status|,V:get_code%
RETURN
You will have to write your own Keyget-Procedure. You can check if any of
the special keys has been pressed, by using BTST(status|,bit) :
bit 0 = Right <Shift>
bit 1 = Left <Shift>
bit 2 = <Control>
bit 3 = <Alternate>
bit 4 = <CapsLock>
You could discover if the user had pressed <Control> <Down arrow> with :
IF scan|=&H50 AND BTST(status|,2)
(...)
ENDIF
If you are only interested in monitoring the five special keys, you could
use BIOS 11 (Kbshift) as well :
status|=BIOS(11,-1)
Use the same table as above to test if bit 0-4 is set.
You are advised to clear the keyboard-buffer before leaving the Procedure
Keyget.
In most cases the scan-code of a key is the same, whether you pressed a
special key simultaneously or not. But watch out for the following
exceptions! For <Shift> <F1> to <Shift> <F10> the scan-codes &H54 to &H5D
are returned (not &H3B to &H44). On an MS-DOS computer these codes are
used for the keys F11 to F20. The combinations <Control> <Left Arrow>
(&H73) and <Control> <Right Arrow> (&H74) also have special codes. Blame
MS-DOS. The combinations <Alternate> <1> to <Alternate> <=> have the
special codes &H78 to &H83. That's ALT1 to ALT= for MS-DOS.
KEYLOOK
According to an unconfirmed report, KEYLOOK does not function properly
with the pre-Blitter TOS.
KEYPRESS
KEYPRESS uses the same 4-byte format as KEYGET: &Hccss00aa. In it you
will recognize the ASCII-code (&Haa), the scan-code (&Hss) and the code
for the special keys (&Hcc). If you want to simulate the pressing of a key
in an Alert box, you will have to send both the ASCII-code and the scan-
code. Use &H1C000D to simulate the pressing of <Return>. Or &H04620062 for
<Control> <Help>, although that certainly won't help in an Alert box. If
you don't need the scan-code (e.g. with INPUT), you can use just KEYPRESS
&Haa.
KEYDEF
The editor always uses KEYPAD &X101110, so you will have to set bit 4
yourself (e.g. KEYPAD &X10000) before you will be able to use KEYDEF in
your program.
Keyboard
As far as I know, there are four different keyboards available : USA
(QWERTY), English (QWERTY), German (QWERTZ) and French (AZERTY). The key
with scan-code &H2B (to the right of <Return>) has a different ASCII-code
in each version :
version ASCII-code character
USA &H5C (92) \
English &H23 (35) #
German &H7E (126) ~
French &H40 (64) @
You could use XBIOS 16 (Keytbl) to determine the keyboard-version :
PROCEDURE keyboard.version
SELECT PEEK(LPEEK(XBIOS(16,L:-1,L:-1,L:-1))+&H2B)
CASE &H5C
usa.keybrd!=TRUE
CASE &H23
english.keybrd!=TRUE
CASE &H7E
german.keybrd!=TRUE
CASE &H40
french.keybrd!=TRUE
ENDSELECT
RETURN
You should take into account the differences between the keyboard-versions
if you are writing a program that should run smoothly in any country.
In the following table I have gathered all keys that have not the same
meaning on the four keyboard-versions :
scancode USA English German French
&H0C - - ß )
&H0D = = - '
&H10 Q Q Q A
&H11 W W W Z
&H15 Y Y Z Y
&H1A [ [ Ü [
&H1B ] ] + ]
&H1E A A A Q
&H27 ; ; ö M
&H28 ' ' Ä \
&H29 ` ` # `
&H2B \ # ~ @
&H2C Z Z Y W
&H32 M M M ,
&H33 , , , ;
&H34 . . . :
&H35 / / - =
&H60 none \ < <
If you are going to use any scan-code from this table, you should be very
careful. It's not nice to ask a German user to press <Y>, but test for
scan-code &H15 in your program...
If you insist on doing things the hard way, you can find the ASCII-value
that is assigned to a key with XBIOS 16. Actually there are three tables:
one for a normal keypress, one for a shifted key and one for a keypress
with CapsLock on :
keytbl%=LPEEK(XBIOS(16,L:-1,L:-1,L:-1))
shift%=keytbl%+&H80
capslock%=shift%+&H80
Now you can find the ASCII-code for any scan-code (< &H80) :
ascii=PEEK(keytbl%+scancode) ! normal key
ascii=PEEK(shift%+scancode) ! shifted key
ascii=PEEK(capslock%+scancode) ! CapsLock on
Keyclick, Keyrepeat and CapsLock
Normally, you need the Keyclick as an audible feedback. Sometimes you have
to switch the Keyclick off, e.g. while an XBIOS 32 song is playing :
PROCEDURE key.click(switch!)
IF switch!
SPOKE &H484,BSET(PEEK(&H484),0) ! keyclick on
ELSE
SPOKE &H484,BCLR(PEEK(&H484),0) ! keyclick off
ENDIF
RETURN
If your program reacts slowly after a keypress, you probably have to
switch the Keyrepeat temporarily off :
PROCEDURE key.repeat(switch!)
IF switch!
SPOKE &H484,BSET(PEEK(&H484),1) ! key-repeat on
ELSE
SPOKE &H484,BCLR(PEEK(&H484),1) ! key-repeat off
ENDIF
RETURN
You can switch CapsLock on or off with :
PROCEDURE caps(switch!)
IF switch!
~BIOS(11,BSET(BIOS(11,-1),4)) ! CapsLock on
ELSE
~BIOS(11,BCLR(BIOS(11,-1),4)) ! CapsLock off
ENDIF
RETURN
9. SCREEN OUTPUT
PRINT
It is very important to know if PRINT will be used on the so-called TOS-
screen (no windows opened), or in a window. TOS emulates the VT52-terminal
of Digital Equipment, so if you PRINT on the TOS-screen, the VT52-codes
will be interpreted as commands. But in a window these codes are printed
as characters!
In both High and Medium resolution you can PRINT 25 lines of 80
characters, but in Low resolution it's 25 lines of 40 characters only.
Normally you can't PRINT a character at position (80,25) in High or Medium
resolution, or at (40,25) in Low resolution. Try the following :
PRINT AT(80,25);"X";
and you will see that a linefeed is executed automatically, in spite of
the semicolon after "X". On the TOS-screen you can put a character at this
position by using the VT52-command 'wrap off' :
PRINT AT(80,25);"*wX"; ! use the Escape-character instead of *
After 'Esc w' the linefeed is suppressed. The easiest way to enter the
Escape-character in the GFA-editor is to hold <Alternate> down and then to
enter <2> <7>. If you PRINT a string that doesn't fit on the current line,
the remaining characters are either printed on the next line ('Esc v', the
default in the interpreter, not in a compiled program), or discarded
('Esc w'). Actually, after 'Esc w' all characters up to the first CHR$(10)
or CHR$(13) are discarded.
It's impossible to PRINT characters with ASCII-code 0-31 on the TOS-
screen. However, you can print any character with :
OUT 5,code
If necessary, use LOCATE first.
After opening a window (OPENW x) the command DEFTEXT will change size and
colour of PRINTed text as well! One advantage is that you can now PRINT in
different colours on the screen.
On the TOS-screen, all PRINTed text has the same colour. This colour is
determined by colour-index 1 and is also used for the Alert-box and the
Fileselector. The background on the screen is determined by colour-index
0. This colour is used after CLS.
It is possible to PRINT in different colours on the TOS-screen, by using
the VT52-code 'Esc b'. The background of PRINTed text can be changed with
VT52-code 'Esc c'. Use the following functions to experiment :
DEFFN ink$(color)=CHR$(27)+"b"+CHR$(color)
DEFFN paper$(color)=CHR$(27)+"c"+CHR$(color)
Use the colour-table from the paragraph 'SETCOLOR' or be prepared to
become very frustrated. I use the Standard Array color.index() as the
colour-table in my programs, so I change the colour of the PRINTed text or
the back-ground with the Standard Functions :
DEFFN ink$(color)=CHR$(27)+"b"+CHR$(color.index(color))
DEFFN paper$(color)=CHR$(27)+"c"+CHR$(color.index(color))
Remember, the VT52-codes have to be PRINTed to become effective, like
this :
PRINT @ink$(red);@paper$(green);" this text is eye-catching "
Note the spaces at beginning and end of the string to emphasize the text-
colour against the background-colour. Of course you have to declare the
variables red and green first (usually colour-index 2 and 3; or if you
don't use the VDI colour-index, try the numbers 1 and 2). I define the
default colours as Standard Globals in my programs. Before I forget, it
won't work in High resolution. Sorry, couldn't resist that one.
In order to catch the eye of the user in High resolution you can PRINT
reverse on the TOS-screen :
PRINT "this is *p IMPORTANT *q" ! enter Esc instead of *
Enter the Escape-character in the usual way. Note again the extra space
both before and after the word that should stand out. Of course you could
also use CHR$(27) :
PRINT "this is ";CHR$(27);"p IMPORTANT ";CHR$(27);"q"
More difficult to read on the screen, but easier to Llist. I use the
Standard Function Rev$ in all programs :
DEFFN rev$(txt$)=CHR$(27)+"p"+txt$+CHR$(27)+"q"
If you use a comma with PRINT, the cursor will jump to the next tabulator-
stop. Tab-stops are at position 1, 17, 33, 49 and 65. Try the following to
see what I mean (in High or Medium resolution) :
PRINT "1","17","33","49","65","1","17"
If you use the comma after the last tab-stop, a linefeed is executed and
the cursor jumps to the first tab-stop on the next line.
The easiest way to use double quotes is by using double double quotes
(read this twice to make sure you understand it) :
PRINT "double quotes printed the ""easy"" way"
In a DATA-line a single double quote suffices :
DATA "look Ma, "double quotes" again"
Or, simply :
DATA look Ma, "double quotes" again
Try the Procedure Fastprint in High resolution if you find PRINT too slow.
This Procedure prints about four times faster! VT52-commands are ignored,
because characters are copied straight from the font-table to the screen.
LOCATE
The syntax of PRINT AT and LOCATE is now less confusing :
PRINT AT(column,line)
LOCATE column,line
In older versions of GFA-Basic it was LOCATE line,column. Check this if
you run an old program and text is PRINTed on the wrong place.
PRINT TAB
PRINT TAB behaves strangely if the position is greater than 80. Try the
following in High or Medium resolution :
FOR i=0 TO 30
PRINT TAB(i*20);i;
NEXT i
One way to solve this problem is :
PRINT TAB(MOD(i*20,80));i; ! do use semicolons
But I think this is a GFA-bug that probably will be corrected in future
versions.
You can combine TAB with PRINT AT and with PRINT USING :
PRINT AT(1,1);"1";TAB(40);"40"
PRINT TAB(40);USING "##",40
Setscreen (XBIOS 5)
With XBIOS 5 (Setscreen) it is possible to change the resolution from Low
to Medium and from Medium to Low. Unfortunately, GEM ignores the switch,
so GEM-commands (e.g. ALERT, TEXT, MOUSE) do not work properly! But you
could change from Low to Medium resolution to show text on the TOS-screen
with PRINT (and VT52-commands). Most users will be grateful for the
improved readability of the text :
~XBIOS(5,L:-1,L:-1,1) ! switch from Low to Medium
(...) ! print text in Medium resolution
~XBIOS(5,L:-1,L:-1,0) ! and go back to Low
If you change the resolution, the VT52-emulator is automatically
initialised. You'll probably have to adjust the palette before you can
read the text without sun-glasses. Don't forget to save and restore the
old palette.
XBIOS 5 is very useful if you would like to draw on a screen before
showing it to the user. Drawing on an "invisible" screen is indeed
possible, because the operating system uses two screens: the physical
screen (visible on your monitor) and the logical screen (usually, but not
necessarily, the same as the physical screen). All graphical (GEM-)com-
mands, including the TEXT-command, are always sent to the logical screen.
But TOS will send PRINT-commands to the physical screen, unless you've
opened a window! If the address of logical and physical screen is not the
same, you have your invisible screen (except for PRINTing on the TOS-
screen). The address of the logical screen must be a multiple of 256.
Study the Procedures Initio.logical.screen, Swap.screen and
Restore.physical.screen to see how you could use the described method for
animation :
PROCEDURE initio.logical.screen
' *** global : SCREEN.1% SCREEN.2%
DIM screen.2|(32256)
screen.2%=VARPTR(screen.2|(0))
screen.2%=screen.2%+256-(screen.2% MOD 256)
screen.1%=physbase%
~XBIOS(5,L:screen.2%,L:-1,-1) ! invisible screen.2 is now active
RETURN
'
PROCEDURE swap.screen
' *** physical and logical screen are swapped
SWAP screen.1%,screen.2%
VSYNC ! avoid flash
~XBIOS(5,L:screen.2%,L:screen.1%,-1) ! swap the screens
RETURN
'
PROCEDURE restore.physical.screen
~XBIOS(5,L:physbase%,L:physbase%,-1)
RETURN
On some ST-computers XBIOS 5 does not function properly after installation
of a RAM-disk. In that case you could change the address of the logical
screen with :
VSYNC
SLPOKE &H45E,adr%
Font
TOS has three built-in system-fonts. The default PRINT-font for High
resolution is the 8x16 font (equals DEFTEXT ,,,13 for TEXT), while the 8x8
font (equals DEFTEXT ,,,6) is used in Medium and Low resolution. You can
switch between these two fonts with the following Procedures :
PROCEDURE font.8x16
LOCAL a$,adr%
a$=MKI$(&HA000)+MKI$(&H2009)+MKI$(&H4E75)
adr%=VARPTR(a$)
adr%=C:adr%() ! address of font-table
{INTIN}={adr%+8} ! pointer to 8x16 system-font
VDISYS 5,2,0,102
RETURN
'
PROCEDURE font.8x8
LOCAL a$,adr%
a$=MKI$(&HA000)+MKI$(&H2009)+MKI$(&H4E75)
adr%=VARPTR(a$)
adr%=C:adr%() ! address of font-table
{INTIN}={adr%+4} ! pointer to 8x8 system-font
VDISYS 5,2,0,102
RETURN
Both Procedures seem to have problems with the accessory QUICKST, but I
never use it with GFA-Basic because there are other problems (INPUT) as
well. If I don't use it, how did I discover this? Good question. The third
font is used for icons, but for some reason can not become the current
system-font. The VDI-function seems to work only with fonts containing
characters of width 8 pixels. The function is not officially documented
by Atari (?).
You can replace the system-font by a font that has been created with
FONTKIT by Jeremy Hughes (4114 byte A1_xxxxx.FON file for High
resolution). Use the Procedure Change.font for this purpose and call the
Procedure Normal.font to restore the original system-font :
PROCEDURE change.font
' *** global : NEW.FONT! NORMAL.FONT%
LOCAL adr%,new.font%
'
' *** load A1_xxxxx.FON file (4114 bytes) here
INLINE new.font%,4114
'
adr%=L~A-22
normal.font%={adr%}
SLPOKE adr%,new.font%
new.font!=TRUE
RETURN
'
PROCEDURE normal.font
IF new.font!
SLPOKE L~A-22,normal.font%
new.font!=FALSE
ENDIF
RETURN
I have not yet discovered how to use a FONTKIT-font with TEXT. Yes, I
could load a new font after installing GDOS, but that's not what I'm
looking for. Is there a quick and not-dirty way to convince GEM that a new
font has been installed?
A font-table for the 8x16 font occupies 4096 bytes (16 bytes/character,
256 characters). A FONTKIT-font usually has a name attached at the end,
that's why I reserve 4114 bytes. TOS ignores the name completely, it's
only used by the accessory FONSEL.ACC. You can load any 4096-byte font in
the Procedure Change.font, you don't even have to change 4114 into 4096.
Although you lose 18 bytes if you don't.
10. PRINTER
Printer ready
If you send data to your printer (HARDCOPY, LPRINT, etc.), your ST will
wait 30 looooooong seconds if the printer happens to be not ready. Always
check if the printer is ready before sending data to the printer, e.g. by
calling the following Procedure :
PROCEDURE printer.ready
LOCAL k
DO
EXIT IF GEMDOS(17)
ALERT 3," printer| | not ready !!",1," OK ",k
LOOP
RETURN
HARDCOPY
You can send a screendump to the printer by using the command HARDCOPY or
by pressing <Alternate> <Help>. In both cases you can abort the screendump
by pressing <Alternate> <Help>.
You can use CONTROL.ACC to change the printer-parameters. Don't forget to
save the desktop, in order to store these parameters in the file
DESKTOP.INF. Look for '#b' with your disk-editor if you're curious. The
parameters will only be read from DESKTOP.INF if CONTROL.ACC is installed
after a reset! I suggest the use of XBIOS 33 (Setprt) in a program instead
of using CONTROL.ACC. For a screendump from the High resolution screen to
an Epson-compatible printer, clear bit 1 and set bit 2 :
PROCEDURE screendump
~XBIOS(33,&X100) ! screendump to Epson(-compatible) printer
HARDCOPY
RETURN
If you set bit 2, TOS assumes you connected an Epson-printer (960
pixels/line). Clear bit 2 and TOS assumes you have an Atari-printer (1280
pixels/line).
If you use HARDCOPY, the width/height ratio of the printout does not
correspond with that of the screen. For a reasonably fast 1:1 screendump,
study the Procedure High.screendump.epson (Epson FX-80, or other 9-pin
Epson-compatible printer) or the Procedure High.screendump.star24 (24-pin
Star LC24-10) :
PROCEDURE high.screendump.star24
LOCAL m$,b$,k,scrn.start%,col,b%,x,d%,p|,b1|,b2|,b3|,n
lf$=CHR$(10)
ff$=CHR$(12)
DEFFN bit.image$(m,d)=CHR$(27)+"*"+CHR$(m)+CHR$(MOD(d,256))+
CHR$(d/256)
DEFFN line.space$(n)=CHR$(27)+"3"+CHR$(n)
initialize$=CHR$(27)+"@"
scrn.start%=XBIOS(2)
'
LPRINT initialize$;
FOR col=0 TO 79
b%=scrn.start%+col
LPRINT SPC(8);
LPRINT @line.space$(24);
LPRINT @bit.image$(33,800);
FOR x=399 TO 0 STEP -1
d%=ADD(b%,MUL(x,80))
p|=BYTE{d%}
IF p|=0
OUT 0,0,0,0,0,0,0
ELSE
CLR b1|,b2|,b3|
IF BTST(p|,0)
b1|=7
ENDIF
IF BTST(p|,1)
ADD b1|,56
ENDIF
IF BTST(p|,2)
ADD b1|,192
b2|=1
ENDIF
IF BTST(p|,3)
ADD b2|,14
ENDIF
IF BTST(p|,4)
ADD b2|,112
ENDIF
IF BTST(p|,5)
ADD b2|,128
b3|=3
ENDIF
IF BTST(p|,6)
ADD b3|,28
ENDIF
IF BTST(p|,7)
ADD b3|,224
ENDIF
OUT 0,b3|,b2|,b1|,b3|,b2|,b1|
ENDIF
NEXT x
LPRINT
EXIT IF INKEY$=esc$
NEXT col
LPRINT ff$;
LPRINT initialize$;
RETURN
For a more flexible approach, study the Procedure Degas.screendump (Star
LC24-10, possible formats : 27x17, 18x11, 13.5x8.5 or 9x5.5 cm).
It should be possible to send only a (GET-)rectangle to your printer with
XBIOS 36 (Prtblk) or V_OUTPUT_WINDOW (VDI 5,Escape 21). Anybody out there
who knows how?
Never, I repeat, never swap disks during a screendump, as this could be
fatal for the new disk. TOS ignores the write-protect state during a
screendump, so it will miss the disk-swap completely. TOS will use the old
FAT for the new disk, and that usually is fatal (no pun intended).
If you have installed a GFA-Basic printer-driver (e.g. PTEPSON.PRG), a
screendump seems to be impossible. The bit-image mode of the printer can't
be used after the driver has been installed.
Printer-commands
Most matrix-printers recognize either IBM- or Epson-commands (or both). I
use the following Procedure to define the most important printer-commands
for my Star LC24-10. Adapt the definitions to your own printer, but stick
to the names for the global variables and functions. Other users could
then use your programs easily after replacing the Procedure Initio.printer
with their own :
PROCEDURE initio.printer
' *** initializes global printer-variables for Star LC24-10
' *** DIP-switch settings :
' 1-1 OFF 2-1 ON
' 1-2 ON 2-2 ON
' 1-3 OFF 2-3 ON
' 1-4 ON 2-4 ON
' 1-5 ON 2-5 OFF
' 1-6 ON 2-6 OFF
' 1-7 ON 2-7 ON
' 1-8 ON 2-8 ON
'
LOCAL c$,f$
c$=CHR$(27)
f$=CHR$(28)
'
draft.char$=c$+"x0"
lq.char$=c$+"x1"
'
courier.style$=c$+"k0"+lq.char$
prestige.style$=c$+"k2"+lq.char$
orator.style$=c$+"k3"+lq.char$
script.style$=c$+"k4"+lq.char$
'
normal.char$=c$+"q0"
outlined.char$=c$+"q1"
shadow.char$=c$+"q2"
outlined.shadow.char$=c$+"q3"
'
italic.on$=c$+"4"
italic.off$=c$+"5"
'
emphasized.on$=c$+"E"
emphasized.off$=c$+"F"
'
underline.on$=c$+"-1"
underline.off$=c$+"-0"
'
bold.draft$=draft.char$+emphasized.on$+double.on$
bold.lq$=lq$+double.on$
bold.off$=emphasized.off$+double.off$
'
superscript.on$=c$+"S0"
superscript.off$=c$+"T"
subscript.on$=c$+"S1"
subscript.off$=c$+"T"
'
epson.set$=c$+"t0"
ibm.set$=c$+"t1"+c$+"6"
DEFFN special.on$(n)=c$+"\"+CHR$(MOD(n,256))+CHR$(DIV(n,256))
DEFFN ibm.special$(n)=c$+"^"+CHR$(n)
DEFFN epson.special$(n)=ibm.set$+@ibm.special$(n)+epson.set$
'
pica$=c$+"P"
'
elite$=c$+"M"
'
condensed.on$=CHR$(15)
condensed.off$=CHR$(18)
'
large.on$=c$+"W1"
large.off$=c$+"W0"
large.line$=CHR$(14)
'
courier.proportional$=courier.style$+c$+"p1"
prestige.proportional$=prestige.style$+c$+"p1"
proportional.off$=c$+"p0"
'
DEFFN master.mode$(n)=c$+"!"+CHR$(n)
underline=128
italic=64
expanded=32
LET double.strike=16
emphasized=8
condensed=4
proportional=2
elite=1
'
normal.size$=c$+"h"+CHR$(0)
LET double.size$=c$+"h"+CHR$(1)
quad.size$=c$+"h"+CHR$(2)
LET double.height$=c$+"w1"
normal.height$=c$+"w0"
normal.width$=f$+"E"+CHR$(0)
LET double.width$=f$+"E"+CHR$(1)
triple.width$=f$+"E"+CHR$(2)
'
lf$=CHR$(10)
DEFFN lf$(n)=c$+"f1"+CHR$(n)
rev.lf$=c$+CHR$(10)
'
ff$=CHR$(12)
rev.ff$=c$+CHR$(12)
'
justify.left$=c$+"a0"
justify.right$=c$+"a2"
justify.full$=c$+"a3"
center$=c$+"a1"
'
reset$=c$+"@"
'
RETURN
Of course you could delete all variables you don't need in your program.
And it really would be nice if everyone uses the proposed names. One of
the strong points of Public Domain GFA-programs is that it's easy to adapt
a program. Or rather, it should be easy. Using the above Procedure is one
step in the right direction.
11. FILES
Floppy Write Test
You are advised to switch the Write Verify test off :
SPOKE &H444,0 ! test off
SPOKE &H444,1 ! test on (default)
According to experts like Dave Small and Bill Wilkinson the Verify test is
a complete waste of valuable time if you write to a disk.
Step Rate
You will find the current step-rate of your drive with :
PRINT DPEEK(&H440)
The following values are possible :
0 - 6 ms
1 - 12 ms
2 - 2 ms
3 - 3 ms (default)
The operating system only looks at this value after a reset (?). For an
external 5.25"-drive you probably have to use 12 ms.
RAM-disk
Drive D is often reserved for a RAM-disk. GFA will recognize a RAM-disk
with DFREE(4) only if it was already present at the time the interpreter
was loaded.
After switching off your 1040 ST you should wait at least 15 seconds
before switching on again. Otherwise an old RAM-disk (or something else in
RAM, e.g. a virus...) may still be present when you switch your computer
on again.
If a RAM-disk is not installed properly after a reset, the reason could be
hidden in the drive bit-table at &H4C2. The old TOS does not clear this
table and a RAM-disk can only be installed as drive D if bit 3 is cleared.
By the way, use drive C (bit 2) only for a harddisk.
DIR$()
Use GEMDOS 25 (Dgetdrv) to find the current drive and combine this with
DIR$(drive) to find the current path :
drive=GEMDOS(25) ! drive 0 - 15
drive$=CHR$(65+drive) ! drive A - P
p$=DIR$(drive+1)
IF p$=""
path$=drive$+":\" ! main directory
ELSE
path$=drive$+":"+p$+"\"
ENDIF
With DIR$(0) you'll find the path of the current drive, not necessarily
drive A. DIR$(1) returns the path of drive A. GEMDOS remembers the last
used path for all available drives. See also the Standard Procedure
Get.path.
If you run GFA-Basic from the main directory and load a GFA-program from a
folder, DIR$(0) will return the nullstring (""), not the folder. After
using CHDIR with the folder-name, the correct path will be returned. I use
CHDIR in the Shell-programs, so the Standard Global default.path$ will
contain the path of the GFA-program. This makes life easier if you want to
load data-files from the same folder, but don't know the precise path when
you write the program. It would be nice if you could determine the path of
the running GFA-program in the program itself.
You can make an array-table of available drives with the aid of BIOS 10
(Drvmap) :
DIM drive!(15)
SELECT DPEEK(&H4A6)
CASE 1
drive!(0)=TRUE
CASE 2
drive!(0)=TRUE
drive!(1)=TRUE
ENDSELECT
table%=BIOS(10)
FOR n=2 TO 15
IF BTST(table%,n)
drive!(n)=TRUE
ENDIF
NEXT n
You can check if a harddisk is connected with :
IF PEEK(&H472)<>0
harddisk!=TRUE
ENDIF
DIR and FILES
After DIR, FILES, TRON or DUMP you can slow down the scrolling with
<CapsLock>. You can temporarily stop the scrolling by holding down the
right <Shift>-key. The output after DIR fits on any screen, but the FILES-
output is too wide for Low resolution. DIR will show only files in the
current directory. FILES will also show folders (marked with *). With DIR
(and FILESELECT) you will not be able to see "hidden" files or "system"
files, but FILES will show all files. You can also search for hidden
and/or system files with FSFIRST and FSNEXT by setting bit 1 and/or bit 2
of the attribute-byte.
Perhaps you have noticed that after the command FILES the first two lines
are peculiar if you happen to be in a folder. The first "name" in a folder
is always '.' (one dot) and the second always '..' (two dots). Time and
date are incorrect, because the authors of (the old) TOS forgot to convert
these to MS-DOS format. In case of nested folders, the operating system
finds the preceding folder through a pointer of the '..'-file. That's why
you can use 'CHDIR ..' to return to the preceding folder. Don't try this
in the main directory, or you'll get an error.
FSFIRST and FSNEXT
The DTA-buffer is usually found at BASEPAGE+128 (it's always there after
start-up), but you should not count on it. Use FGETDTA() to find the
current address, before FSFIRST :
dta.adr%=FGETDTA()
e%=FSFIRST(format$,attr)
FSFIRST returns -33 if no file has been found. FSNEXT returns -49 if no
more files are found.
In an accessory it's safer to create a new DTA-buffer :
old.dta%=FGETDTA() ! old buffer
dta$=STRING$(44,0)
dta.adr%=V:dta$
~FSETDTA(dta.adr%) ! new buffer
(...) ! FSFIRST/FSNEXT
~FSETDTA(old.dta%) ! restore old buffer
The Desktop will appreciate all this extra work.
The 44 byte DTA-buffer (Data Transfer Address) contains the following data
after a succesful FSFIRST or FSNEXT :
offset
0-20 - reserved
21 - attribute-byte
22-23 - time
24-25 - date
26-29 - file-length
30-43 - filename (including the extension, terminated by &H00)
The attributes and the filename can be read from the buffer with :
attr=BYTE{dta.adr%+21}
file$=CHAR{dta.adr%+30}
The DTA-buffer is not an exact copy of the relevant information in the
directory of the disk. With a disk-editor you would find a slot of 32
bytes for each file or folder :
offset
0- 7 - file- or folder-name (without extension)
8-10 - extension
11 - attribute-byte
12-21 - reserved
22-23 - time
24-25 - date
26-27 - FAT-pointer
28-31 - file-length
The first byte of the filename has a special meaning in the following
cases :
&H00 - free slot, never used before
&HE5 - erased file, now free slot
&H2E - subdirectory
Both time and date are stored in MS-DOS format. Consult your GFA-manual
for more information. The FAT-pointer, also in an MS-DOS format (Intel-
format : first low byte, then high byte), points to the first cluster of
the file. If you are looking at a folder, the FAT-pointer points to the
cluster where you will find the directory of this folder (subdirectory).
If you are looking at a subdirectory (i.e. you are in a folder), the first
two slots are reserved for the files '.' and '..' (&H2E and &H2E2E). This
has already been mentioned in the paragraph 'DIR and FILES'. Finally, the
file-length is stored in, you guessed it, MS-DOS format. You might wonder
what MS-DOS has to do with Atari-disks. Read the paragraphs 'Disk Format'
and 'File Allocation Table' for the explanation.
If you use the attribute-byte &X10000 you will find both folders and
files! If the folders in a directory don't have an extension and all files
do have an extension, you could find all folders in the main directory as
follows :
e%=FSFIRST("\*",&X10000) ! and e%=FSNEXT() for next folders
If you can't use this simple method, you'll have to check after each
successful FSFIRST/FSNEXT if it's a folder or a file :
IF BTST(BYTE{dta.adr%+21},4)
(...) ! yes, it's a folder
ENDIF
Use the attribute-byte 0 (i.e. no bits set) to find ordinary files only.
Use attribute-byte &X1000 to find the disk-name :
dta.adr%=FGETDTA()
e%=FSFIRST("\*.*",&X1000) ! finds disk-name only, not files
disk.name$=CHAR{dta.adr%+30}
You can read the attributes of a file or folder with GEMDOS 67 (Fattrib) :
attr%=GEMDOS(67,L:V:filename$,0,0)
If the file (or folder) is not found, attr% is -33 (or -34), otherwise
attr% contains the attributes in the usual format. You can even change the
attributes of files with :
r%=GEMDOS(67,L:V:filename$,1,attribute%)
It's not possible to change the attributes of folders or the disk-name
with GEMDOS 67. One way to do that is to change the directory-sector with
the aid of BIOS 4 (Rwabs). If you really feel the urge to experiment, you
should realize that one tiny mistake could ruin the disk.
EXIST
You can use EXIST to test if a folder exists, but only if the folder
contains at least one file :
IF EXIST("\FOLDER\*.*")
(...) ! folder found
ELSE
(...) ! folder not found or empty folder
ENDIF
LOF
The length of a file is easily determined with LOF :
OPEN "I",#1,file$
length%=LOF(#1)
CLOSE #1
To determine the number of records in a random file you could divide the
file-length by the total FIELD-length.
TOUCH
Use this method with TOUCH :
OPEN "U",#1,file$
TOUCH #1
CLOSE #1
NAME
With the old TOS you can only change the name of files, not of folders.
Even from the desktop you can't change the name of a folder, so choose it
carefully.
KILL
KILLing a file does not erase it from the disk. The first byte of the
filename is changed to &HE5. Unfortunately you can't restore a killed file
by simply changing this byte with a disk-editor. The operating system will
be able to find the first cluster of the restored file, because the first
FAT-pointer is located in the directory. The next clusters can only be
found through the FAT (File Allocation Table), but after KILL all pointers
to this file are irreversibly erased. If you have not killed any file on
the disk before your fatal mistake, you are extremely lucky and will find
all clusters have been stored consecutively. But after some killing and
saving on the disk, the file could be dispersed over the entire disk. Some
programs are able to help you, but you will have to recognize clusters as
belonging to the killed file. That's easy with ASCII-files, but almost
impossible with other files. So, don't KILL unless you have to.
File Copy
You can copy a file source$ to dest$ (use complete pathnames!) with :
PROCEDURE file.copy(source$,dest$)
LOCAL block%
OPEN "I",#90,source$
OPEN "O",#91,dest$
block%=LOF(#90)
WHILE block%>32000
PRINT #91,INPUT$(32000,#90);
SUB block%,32000
WEND
PRINT #91,INPUT$(block%,#90);
CLOSE #90
CLOSE #91
RETURN
Do not copy a file "to itself" on a harddisk. Thanks to yet another bug in
TOS, this action could completely wipe out the harddisk. Or perhaps this
should be called a feature of TOS, put there to punish the crazy user who
tries to copy a file to itself.
Disk Format
A disk contains 80 concentric tracks (numbered 0 - 79) or more. Sometimes
the expression "cylinder" is used instead of "track". Each track is
divided into 9, 10 or even 11 sectors. One sector can contain 512 data-
bytes. In order to be compatible with MS-DOS, TOS formats a disk with 80
tracks and 9 sectors/track. Actually it's easy to fit 10 sectors in one
track. With a little more effort you can create room for 11 sectors, but
some drives run slightly too fast and are not able to read the 11th
sector!
With a disk-editor you can examine the 512 data-bytes of a sector, but
you can't examine the sector-layout without accessing the Floppy Disk
Controller (FDC) directly. In that case you would find the following
layout for each sector :
data-separator (GAP) - 15 bytes
ID-Address mark - 1 byte
sector-header - 4 bytes (track, side, sector, size)
CRC of sector header - 2 bytes
data separator - 37 bytes
Data-Address Mark - 1 byte
data bytes - 512 bytes
CRC of data bytes - 2 bytes
data separator - 40 bytes
The data separator bytes are there to synchronize the FDC properly. The
FDC recognizes the sector-header by the preceding ID-Address mark. The
sector-header itself contains information about the current track, side
and sector and also about the size of the data-field (usually 512 bytes).
The FDC checks both the sector-header and the data-field for corrupted
bytes by comparing a computed "checksum" with the stored CRC-value. The
operating system cannot read/write one byte from/to a sector, only
complete sectors are read or written. GFA-Basic takes care of all the
dirty work.
First some bad news. A CRC-error is not always recognized by the ROM of
520 ST's and 1040 ST's (bug in XBIOS 8, Floprd). If your palms are now
getting sweatty, you could check your most precious disks with XBIOS 19
(Flopver). This function checks for 'lost data, RNF- or CRC-errors'.
Create a buffer of 1024 bytes and call XBIOS 19. A sector (512 bytes) is
loaded from the disk in drive A or B (0 or 1) into the second part of the
buffer and checked. If a bad sector is found, the sector-number is stored
as a word in the first part of the buffer. After checking all sectors in
one track you have to examine the word-list in the buffer. Hope you will
find only &H0000 there. I leave the writing of this program as an exercise
to the reader. Never thought I would use that phrase myself. Allright,
here's something to get started :
buffer$=STRING$(1024,0)
adr%=V:buffer$
r%=XBIOS(19,L:adr%,L:0,drive,1,track,side,9) ! if 9 sectors/track
You should now be able to find out if the track on this side (0 or 1) is
OK. Good luck.
You can use BIOS 7 (Getbpb) to examine the disk-format in the so-called
BIOS-Parameter-Block (BPB) of the disk :
bpb.adr%=BIOS(7,drive) ! address of BPB, or 0 (= error)
In 9 words you'll find the following information in the BPB :
offset
0 - bytes/sector (usually 512)
2 - sectors/cluster (usually 2)
4 - bytes/cluster
6 - number of directory-sectors
8 - length of FAT
10 - first sector of second FAT
12 - first data-sector
14 - total clusters
16 - flag (not used)
Use GEMDOS 54 (Dfree) to find out how many free clusters are available, or
simply use DFREE if you want to know how many free bytes are available on
a drive. Due to a bug, GEMDOS misses the last two clusters on a disk. You
can't write to these clusters (2048 bytes down the drain...), but you can
read these clusters if they do contain data. That would be a miracle, or
an MS-DOS disk.
You'll probably use XBIOS 10 (Flopfmt) to format a disk from GFA-Basic. If
you do, use &H0000 as the virgin-value for the first 18 sectors, and then
&HE5E5 for all data-sectors. You can use either 9 or 10 sectors/track, not
11. If you use 10 sectors/track, you should fill the first two tracks with
&H0000 and fill sectors 19 and 20 with &HE5E5 afterwards (read the
paragraph 'Sectors'). I don't recommend more than 80 tracks, certainly not
more than 82 tracks. If XBIOS 10 returns a value other than 0, you'll find
a list of bad sectors in the buffer you used (terminated with &H00). If
one of the first 18 sectors is bad, you can throw the disk away.
Using XBIOS 10, the interleave-factor should be 1. This means the sectors
on a track are numbered consecutively: 1,2,3,4,etc. I don't understand
why some programmers use another value and call it 'Fast Format'. The FDC
needs more time to read a complete track if the interleave-factor is not
1. Perhaps they try to create the so-called Twisted (or Skewed) format.
TOS loses time if the head moves to the next track. Because the Seek with
Verify flag is set, the FDC first verifies the track-number and then reads
the sector-number. While checking the track-number, sector 1 was passing,
so we have to wait for one complete spin of the disk (200 ms, yawn) until
sector 1 can be read. One solution is to clear the Seek with Verify flag,
but that could lead to nasty problems if the head still rattles slightly
at the time sector 1 is read. The best solution is the Twisted format
(adopted by Atari from the Mega ST onwards). For a single-sided disk with
9 sectors on one track this means :
track 0 : sector 1,2,3,4,5,6,7,8,9
track 1 : sector 8,9,1,2,3,4,5,6,7
track 2 : sector 6,7,8,9,1,2,3,4,5
etc.
Now, sector 1 is encountered almost immediately after the track-number is
verified. A 1-sector offset is not possible, but a 2-sector offset is
enough to settle the rattling head. It is impossible to read data faster
from a disk! But I'm afraid you can't format a Twisted disk with XBIOS 10
if you have an old TOS. You have to use a special format-program.
If you format a disk from the desktop, bad clusters are flagged with a
special value in the FAT. However, if TOS 1.4 encounters a bad sector,
something goes wrong and the FAT is corrupted. One more reason to quit
smoking, because smoke-particles definitely constitute a serious hazard to
the health of your disks.
You can use XBIOS 18 (Protobt) to create a bootsector on the formatted
disk. Don't worry about the media-byte (disk-type), because TOS doesn't
use it. Do use &H1000000 to generate a random serial number, because it is
very important that different disks have different serial numbers! Write
the bootsector to the disk with XBIOS 9 (Flopwr), not with BIOS 4 (Rwabs):
r%=XBIOS(9,L:buffer%,L:0,drive%,1,0,0,1)
Why are different serial numbers so important? If TOS suspects a disk-
swap, the serial number is read from the disk. A disk-swap can only be
recognized if the number on the new disk is different from the old number.
If the new disk contains the same serial number, TOS uses the FAT of the
previous disk. Writing to a swapped disk will probably zap it, if you
follow me. Disk-copiers copy everything, including serial numbers. Be
careful!
Although it is possible to format a disk from GFA-Basic, I don't recommend
it (now he tells us...). My favourite format is :
80 tracks
10 sectors/track
Twisted format
I use TWISTER.PRG (not Public Domain, as far as I know), but you could use
a Public Domain program like DCOPY (actually Shareware). The Desktop can't
copy a Twisted disk, but file-copy is always possible. DCOPY copies any
format, including Twisted format.
File Allocation Table (FAT)
The first sector on a disk is the boot-sector. The next five sectors are
reserved for the FAT. And the next five for a copy of the FAT (actually
it's the other way around). Finally, the main (or root) directory occupies
the next 7 sectors (32 bytes for each slot). This means that the first 18
sectors (No. 0-17) are reserved for the operating system. All other
sectors are available for storing files.
The storage-unit for files is a cluster. A cluster consists of two
consecutive sectors (1024 bytes). A file that contains only 1 byte will
therefore still occupy 1024 bytes (1 K) on the disk. When a file is saved,
the operating system looks for the first empty cluster, then the next,
etc. Information about available clusters is stored in the FAT as a
collection of pointers. Due to inefficient programming, the search for
free clusters takes a long time. Try DFREE on a harddisk and you'll agree.
Install a program like FATSPEED.PRG (Public Domain, by Ulrich Kuebler) to
speed this up!
The first three bytes of the FAT are not used by TOS, but are there to
enable MS-DOS to read an ST-formatted disk. TOS writes &HF7FFFF, where the
first byte (&HF7) is supposed to be the media-byte. Unfortunately MS-DOS
doesn't understand this and refuses to read the directory properly. I'm
not quite sure why, but changing the first three bytes to &H000000 seems
to work. You could try &HF8FFFF (80 tracks, 9 sectors/track, single sided
disk) or &HF9FFFF (double sided disk) instead. Or you could use the media-
byte at offset 21 from the bootsector. To make your MS-DOS friends
completely happy, you should change the first three bytes of the bootsec-
tor to: &HEB3890. Sometimes (DOS 4.0 ?) you have to change the next three
bytes as well: &H49424D (that's IBM in ASCII; please watch your language).
Better still, let them use their own disk editor on their MS-DOS computer
(e.g. Norton Utilities). Why should you do all the work? By the way, your
ST should be able to use an MS-DOS disk without any modifications.
Each FAT-pointer consists of one and a halve byte (3 nibbles, i.e. 12
bits). In hexadecimal notation this means three digits for one pointer.
The first pointer (No. 0) points to cluster No. 0, the second to No. 1,
etc. . Because the first two pointers don't count, you have to subtract
two to find the actual cluster (pointer 2 points to cluster No. 0, etc.).
The first cluster starts at sector No. 18 (remember, sectors 0-17 are
reserved for bookkeeping), so you could find the first sector of a cluster
with :
(pointer - 2) * 2 + 18
The second sector of this cluster is of course the next one.
TOS reads the pointers in a peculiar (MS-DOS) way. Suppose the FAT-sector
starts with :
F7 FF FF 03 40 00 FF 0F 00
Without further explanation, this translates to :
FAT-pointer 0 and 1 are ignored
FAT-pointer 2 = &H003 (next cluster on sector 20 + 21)
FAT-pointer 3 = &H004 (next cluster on sector 22 + 23)
FAT-pointer 4 = &HFFF (last cluster of this file)
FAT-pointer 5 = &H000 (free cluster)
In order to understand this, you have to consult the following table :
&H000 : cluster still available
&HFF1 - &HFF7 : bad cluster, never available
&HFF8 - &HFFF : last cluster of this file (End Of File)
&H002 - &HFF0 : pointer to next cluster
Assuming the FAT-pointer &H002 at offset 26 in the directory (you'll find
&H0200 with a disk editor, that's Intel format with low byte first again),
you should be able to figure out that this file will be found in sectors
18 through 23. Sectors 24/25 are empty, so this cluster is available for a
new file. Any questions? The one big advantage of all this is compatibi-
lity with MS-DOS disks. With TOS 1.4 your ST-disks can be made completely
compatible, so you don't even have to change a few bytes.
Right, you have just read the Fatman-book. The movie will be released
shortly, but you can NOW buy those fabulous Fatman-shirts and fantastic
Fatman-buttons. Call Atari for further details.
Sectors
There are two different methods to assign a number to a sector. The first
one is to number the "physical" sectors in each track from 1 to 9
(assuming 9 sectors/track). This way you could say the bootsector is
sector 1 on track 0 (on side 0 if the disk is double sided). But GEMDOS
doesn't care about tracks or sides, it counts "logical" sectors from 0 to
719 (80 tracks, 9 sectors/track, one sided disk) or from 0 to 1439 (double
sided disk). According to GEMDOS, the bootsector is on sector 0. And on a
double sided disk, physical sector 1 on track 0 of side 1 (the other side)
would be sector 9.
With BIOS 4 (Rwabs) you can read (and write) complete logical sectors :
buffer$=SPACE$(512) ! 512 bytes for 1 sector
buffer%=V:buffer$ ! address of buffer
r%=BIOS(4,0,L:buffer%,1,sector%,drive%) ! load the sucker
You would load the bootsector from the disk in drive A with :
r%=BIOS(4,0,L:buffer%,1,0,0)
You can use BIOS 4 not only with floppy disks, but also with a harddisk
or a RAM-disk. After loading a sector, you can read one byte with :
b|=BYTE{buffer%+n} ! n from 0 to 511
You can read a word with :
w=WORD{buffer%+n}
But only if the word starts at an even address. Otherwise you have to use:
w=BYTE{buffer%+n+1}+256*BYTE{buffer%+n}
If necessary, you can speed this up by using the special integer commands
for addition and multiplication. How about this Polish monster :
DEFFN word(adr%)=ADD(BYTE{SUCC(adr%)},MUL(256,BYTE{adr%}))
Then you would use :
w=@word(ADD(buffer%,n))
If you use BIOS 4 to write a sector after formatting a disk, you should
use '3' as a flag (not '1') :
r%=BIOS(4,3,L:buffer%,1,sector%,drive%)
You can also read and write physical sectors with XBIOS 8 (Floprd) and
XBIOS 9 (Flopwr). With these commands you could even read/write all
sectors on one track. This is the only way to read a track, because
reading a complete track is impossible due to a bug in the FDC.
If you swap a disk after loading/writing a sector you should be careful.
Testing with BIOS 9 (Mediach) you could miss the disk-swap. This could be
fatal, because TOS uses the FAT of the other disk! I think you could use
BIOS 7 (Getbpb) in most cases, but use the Procedure Force.mediach if in
doubt :
PROCEDURE force.mediach
LOCAL x$,old.vector%,a%
x$=SPACE$(12)
old.vector%=LPEEK(&H47E)
a%=V:x$
DPOKE a%,&H2B7C
LPOKE a%+2,old.vector%
DPOKE a%+6,&H47E
DPOKE a%+8,&H7002
DPOKE a%+10,&H4E75
SLPOKE &H47E,a%
~DFREE(0) ! current drive
RETURN
To be more precise, BIOS 7 can't be used in the following situation :
- TOS reads a sector
- you swap disks
- you use BIOS 7 in your program
- TOS reads a sector
Now TOS will assume there has been no disk-swap, because there has been no
disk-swap after the last BIOS 7 call! You definitely need the Procedure
Force.mediach in this case.
By the track, TOS also ignores disk-swaps during a screendump (HARDCOPY),
or while using DMA (harddisk, laser printer). TOS detects a disk-swap by
monitoring the write-protect state. To see this, you should turn all
lights off and then watch the drive-light closely. Closer. You can turn
the lights on again. If the drive is empty, TOS gets a write-protect
signal and assumes the user might have swapped disks. BIOS 9 should return
'1' at this point. TOS checks if you really did swap disks by reading the
serial number from the bootsector and comparing it with the current
number. Only if these numbers are different, a disk-swap is recognized by
TOS (BIOS 9 should return '2' now). You probably deduced that TOS will
read the bootsector also if you use a write-protected disk. Continuously
reading the bootsector is a waste of time, so TOS waits 1.5 seconds before
looking again. Never swap disks within 1.5 seconds after a read/write-
operation. The drive keeps spinning for 2 seconds, so you can't go wrong
if you wait until the drive-light is off before swapping disks.
Bootsector
In the following table you'll find the lay-out of a bootsector. All words
are in Intel-format, except CHKSUM.
offset length name
0 2 &H6038 = branch to bootroutine
2 6 FILLER fill-bytes
8 3 SERIAL serial-number of disk
11 2 BPS bytes/sector (512)
13 1 SPC sectors/cluster (2)
14 2 RES reserved sectors (1, Bootsector)
16 1 NFATS number of FAT's (2)
17 2 NDIRS max. entries in main directory
19 2 NSECTS total sectors
21 1 MEDIA media-byte (not used by TOS)
22 2 SPF sectors/FAT (5)
24 2 SPT sectors/track
26 2 NSIDES sides (1 or 2; no joke this time)
28 2 NHID hidden sectors (ignored by TOS)
30 2 EXECFLAG start of bootcode : flag
32 2 LDMODE 0=load FNAME; <>0=load sectors
34 2 SSECT first sector (LDMODE<>0)
36 2 SECTCNT number of sectors
38 4 LDADDR load at this RAM-address
42 4 FATBUF address of FAT-buffer
46 11 FNAME filename (nnnnnnnneee) (LDMODE=0)
57 1 DUMMY fill-byte
58 boot-routine (could be a boot-virus)
510 2 CHKSUM
TOS determines if the bootsector is executable by adding all bytes. If
this sum (AND &HFFFF) equals &H1234, the bootsector is executable. If you
use GFA-Basic this probably means you have an ancient ST with TOS in RAM,
or a boot-virus. A normal GFA-disk has only &H00- or &HE5-bytes from the
offset 58.
BLOAD
BLOAD needs an address, unless you have used BSAVE in the same program
before. In that case the BSAVE-address is used automatically by BLOAD if
you don't specify a new address.
BLOAD (BSAVE) is easier to use than BGET (BPUT), because you don't have to
open the file. But with BLOAD you can only load the entire file, while
BGET allows you to load any part of the file.
INP and OUT
Both INP and OUT can also be used with 2-byte and 4-byte integers :
a|=INP(#n)
a&=INP&(#n)
a%=INP%(#n)
OUT #n,a|
OUT& #n,a&
OUT% #n,a%
INPUT and LINE INPUT
Because GFA now uses a 4K-buffer for each opened file (version 3.07),
reading data from a file with (LINE) INPUT goes much faster.
STORE and RECALL
For very fast loading and saving of string-arrays, you should use RECALL
and STORE. You can also store (or recall) a part of an array as follows :
STORE #1,txt$(),10 ! store elements 0 through 9
STORE #1,txt$(),5 TO 10 ! store elements 5 through 10
The correct syntax is :
STORE #i,x$()[,n[ TO m]]
RECALL #i,x$(),n[ TO m],x% (use n=-1 for complete array)
Of course you have to open the file first.
If you STORE a text-array, GFA puts &H0D0A (CHR$(13);CHR$(10)) after each
element. This is the same format as used by 1st Word Plus (WP Mode off,
i.e. ASCII-mode), Tempus, etc.
If you're going to show more than a few text-lines, you could enter the
text as DATA-lines, e.g. with the Procedures Initio.text.array and
Initio.text.array.low (for Low resolution) :
PROCEDURE initio.text.array
' *** global : TEXT$()
LOCAL lines,line$,n
lines=0
RESTORE txt.data
READ line$
REPEAT
INC lines
READ line$
UNTIL line$="***"
ERASE text$()
DIM text$(lines)
RESTORE txt.data
FOR n=1 TO lines
READ text$
text$(n)=SPACE$(5)+text$ ! left margin of 5 spaces !!
NEXT n
txt.data:
DATA text
DATA ***
RETURN
If you are going to show more text, I suggest you use 1st Word Plus, or
any other wordprocessor or text-editor that can save your text as an
ASCII-file. With 1st Word Plus, I use a Ruler length of 70 and the
following Page Layout Form :
Paper length 66
TOF margin 19
Head margin 4
Foot margin 4
BOF margin 19
Lines/page 20
Enter the text and save as an ASCII-file (turn WP Mode off before saving).
In your GFA-Basic program you would first load the text in a string-array:
DIM text$(lines)
OPEN "I",#1,file$
RECALL #1,text$(),-1,lines% ! lines% ≤ lines+1
CLOSE #1
Then you could use the Procedure Show.text.page (High or Medium resolu-
tion) to show the text with 20 lines/screen. The Procedure uses a left and
right margin of 5 characters, so that's why you have to use 70 charac-
ters/line in your wordprocessor. If the text-array is full, you won't get
an error if the file contains more text-lines!
You could use a 2-dimensional string-array to store first and last names :
name$(i,0)=first_name$
name$(i,1)=last_name$
Again, STORE and RECALL are very fast. But it is now necessary to use
exactly the same dimensions with RECALL that you used with STORE. If the
dimensions don't match, the array will be scrambled after RECALL.
FILESELECT
Don't use the underscore '_' in the path-line of the Fileselector, because
a bug in the old TOS will then cause a few bombs. Owners of a Mega ST or
TOS-version 1.4 can use as many underscores as they like.
Changing drives in the Fileselector (old TOS) is not easy. Click on the
path-line and press <Esc> to clear the line. Enter the drive, e.g. 'D:\',
and click on the bar under the path-line in order to read the new
directory. Also click on this bar after changing disks (in this case you
would press <Esc> on the desktop). Selecting drive A is easier: just clear
the path-line and click on the bar. The TOS-code that takes care of all
this work is also known as the bartender.
If you have changed the extension in the path-line, you should click just
below the bar. If you click on the bar, the path-line is overwritten with
'*.*' ! That same bartender strikes again.
The correct syntax for calling the Fileselector is :
FILESELECT [#title$,]path$,default$,file$
The title is only used in TOS-version 1.4 and ignored in older TOS-
versions. The default$ usually is the null-string (""), but don't use the
null-string for path$ or the Fileselector will freeze. Use "\*.*" as path$
for all files in the main directory. Do use the backslash in the pathname
(e.g. "A:\*.*"). Due to a bug in GEM, the wrong drive is sometimes used if
you forget the backslash ("A:*.*"). If the user has selected a file, file$
will contain the path and filename. The file$ will be the null-string if
the user selected <Cancel>. A third possibility is easily overlooked: the
user could have selected <OK> without choosing a file. In that case file$
contains the current path, ending with a backslash.
Do not despair if you need a title, but don't have TOS 1.4. Try the
Procedure Fileselect (High or Medium resolution) or Fileselect.low :
PROCEDURE fileselect(path$,default$,txt$,left$,right$,VAR file$)
' *** print optional title (light text) to left and right
LOCAL screen$,y.fac
SGET screen$
CLS
IF high.res!
y.fac=1
ELSE
y.fac=2
ENDIF
DEFTEXT black,2,900,32
TEXT 100,350/y.fac,300/y.fac,left$
DEFTEXT ,,2700
TEXT 540,50/y.fac,300/y.fac,right$
DEFTEXT ,0,0,13
PRINT AT(1,3);@center$(txt$)
GRAPHMODE 3
DEFFILL black,1
BOUNDARY 0
IF high.res!
BOX 157,25,482,54
PLOT 157,25
PBOX 159,27,480,52
ELSE
BOX 157,12,482,27
PLOT 157,12
PBOX 160,14,479,24
ENDIF
BOUNDARY 1
GRAPHMODE 1
FILESELECT path$,default$,file$
SPUT screen$
RETURN
As a programmer, you should take into account the possibility that a user
might start your program from drive A, a harddisk or a RAM-disk. I use the
Standard Global default.path$ to remember where the program was started.
If the user changes the (default) path in the Fileselector, you should
note the change and use the new path if the Fileselector is called again.
Use the Procedure Parse.filename for this purpose :
PROCEDURE parse.filename(parse.name$,VAR drive$,path$,file$,ext$)
LOCAL pos,first,last,last!,search,parse.file$
'
parse.name$=UPPER$(parse.name$)
IF MID$(parse.name$,2,1)=":"
drive$=LEFT$(parse.name$,1)
ELSE
drive$=CHR$(65+GEMDOS(&H19)) ! current drive
ENDIF
'
pos=1
last!=FALSE
last=0
first=INSTR(1,parse.name$,"\")
REPEAT
search=INSTR(pos,parse.name$,"\")
IF search>0
pos=search+1
last=search
ELSE
last!=TRUE
ENDIF
UNTIL last!
IF last>0 ! backslash discovered
path$=MID$(parse.name$,first,last-first+1)
parse.file$=MID$(parse.name$,last+1)
ELSE ! no '\'
path$=""
pos=INSTR(1,parse.name$,":")
IF pos>0
parse.file$=MID$(parse.name$,pos+1)
ELSE
parse.file$=parse.name$
ENDIF
ENDIF
pos=INSTR(parse.file$,".")
IF pos>0 ! name with extension
ext$=MID$(parse.file$,pos+1)
file$=LEFT$(parse.file$,pos-1)
ELSE ! name without extension
ext$=""
file$=parse.file$
ENDIF
RETURN
If you have a joystick with Auto-Fire on, you should switch it off. The
Fileselector doesn't like Auto-Fire. Neither do I.
The Fileselector will warn you with a modest 'ping' if it counts more than
100 files/folders. It will show only the first 100 files/folders. I think
45 files in one folder is really the limit for impatient users like
myself. More than 100 is a crime that should be punished with more than a
'ping'. The main directory of drive A can't contain more than 112
files/folders, because the 7 directory-sectors contain 112 32-byte slots.
Every time you open a folder (in the Fileselector, or otherwise), TOS
stores information about the folder in a table. After opening/accessing 40
folders, TOS will delete clusters, cross-link clusters, and do other nasty
things. Your disk could be completely destroyed, thanks to this bug. Atari
enlarged the buffer in TOS 1.2 and fixed the bug in TOS 1.4. Atari also
distributes the program FOLDRxxx.PRG to extend the 40-folder limit with
100 (FOLDR100.PRG) or more. Be careful with SHOW INFO, it's easy to exceed
the 40-folder limit! You could recognize a disaster by one of the
following symptoms :
- unexpected '0 bytes in 0 items' message in directory
- folder-names trashed (usually lots of Greek letters)
- Show Info crashes or shows weird information
Don't be afraid of a new virus. It's only a TOS-bug. Immediately reset
your ST, try to salvage as many files as possible and reformat the disk.
If all files are lost, you will have to use your back-up files. If you
don't have back-up files, you have nothing left but my sympathy.
12. MIDI
INPMID$
With the command INPMID$ the internal Midi-buffer is read and at the same
time cleared. You can find the buffer with XBIOS 14 (Iorec) :
adr%=LPEEK(XBIOS(14,2))
The default size of this buffer is only 128 bytes, but you can use the
Procedure Change.midi.buffer to change the size of the buffer :
PROCEDURE change.midi.buffer(size%)
LOCAL ptr%
ptr%=XBIOS(14,2)
ERASE buffer|()
DIM buffer|(size%-1)
LPOKE ptr%,VARPTR(buffer|(0)) ! start-address of new buffer
DPOKE ptr%+4,size% ! size
DPOKE ptr%+6,0 ! buffer-head
DPOKE ptr%+8,0 ! buffer-tail
DPOKE ptr%+10,0 ! low mark (not used)
DPOKE ptr%+12,size%-1 ! high mark (not used)
RETURN
Because handshake is impossible with Midi, you need a large buffer if
Midi-bytes are coming in fast.
INP
If you use INP(3) to read Midi-bytes, you should first check with INP?(3)
if the Midi-buffer contains data. If you use INP(3) and no bytes are
available, your ST will freeze. Until you reset the computer.
Midi-commands
Study the Procedures in the file MIDI.LST to see how you can use Midi-
commands in GFA-Basic. With the Procedures Record.midi and Play.midi you
could write a simple Midi-recorder. And the Procedure Midi.monitor can be
used to examine incoming Midi-messages.
PROCEDURE record.midi(VAR midi.byte|(),midi.time%())
' *** uses Procedure All.midi.off
' *** global : LAST.MIDI.BYTE%
LOCAL last%,buffer$,i%,t%,time%,byte|,delay%,j%,m$,k
ARRAYFILL midi.byte|(),0
ARRAYFILL midi.time%(),0
last%=DIM?(midi.byte|())-1
m$=STR$(last%)+" bytes available ;| |stop recording|"
m$=m$+"by pressing space"
ALERT 3,m$,1,"RECORD",k
REPEAT
UNTIL INKEY$=""
buffer$=INPMID$ ! clear MIDI-buffer
i%=1
t%=TIMER
REPEAT
IF INP?(3)
byte|=INP(3)
IF byte|<>254
time%=SUB(TIMER,t%)
midi.byte|(i%)=byte|
midi.time%(i%)=time%
INC i%
ENDIF
ENDIF
UNTIL i%=last% OR INKEY$=" "
last.midi.byte%=i%-1
@all.midi.off
delay%=midi.time%(1) ! subtract time for first note
FOR j%=1 TO i%
SUB midi.time%(j%),delay%
NEXT j%
m$="|"+STR$(i%)+" bytes recorded"
ALERT 3,m$,1," OK ",k
RETURN
'
PROCEDURE play.midi(VAR midi.byte|(),midi.time%())
' *** uses Procedure All.midi.off
LOCAL m$,k,i%,t%,time%
m$="record of "+STR$(INT(midi.time%(last.midi.byte%)/200))+
m$=m$+" seconds| |(stop by pressing space)"
ALERT 3,m$,1,"PLAY",k
REPEAT
UNTIL INKEY$=""
i%=1
t%=TIMER
REPEAT
time%=SUB(TIMER,t%)
IF midi.time%(i%)<=time%
OUT 3,midi.byte|(i%)
INC i%
ENDIF
UNTIL i%=last.midi.byte% OR INKEY$=" "
@all.midi.off
RETURN
'
PROCEDURE midi.monitor
LOCAL m$,k,byte|,byte$,hex$,bin$,buffer$,key$
byte$=SPACE$(3)
hex$=SPACE$(2)
bin$=SPACE$(8)
m$="all incoming bytes|(except 254) are|printed on screen ;|"
m$=m$+"stop by pressing space"
ALERT 1,m$,1,"START",k
m$="press|<Return>|for CLS"
ALERT 1,m$,1," OK ",k
REPEAT
UNTIL INKEY$=""
buffer$=INPMID$ ! clear MIDI-buffer
CLS
PRINT TAB(10);"dec";TAB(20);"hex";TAB(30);"binary"
REPEAT
REPEAT
key$=INKEY$
IF INP?(3)
byte|=INP(3)
IF byte|<>254
IF TIMER-t%>200
PRINT
ENDIF
t%=TIMER
RSET byte$=STR$(byte|)
RSET hex$=HEX$(byte|)
RSET bin$=BIN$(byte|)
PRINT TAB(10);byte$;TAB(20);hex$;TAB(30);bin$
ENDIF
ENDIF
UNTIL key$=" " OR key$=CHR$(13)
IF key$=CHR$(13)
CLS
ENDIF
UNTIL key$=" "
@all.midi.off
REPEAT
UNTIL INKEY$=""
PRINT " (press any key)"
~INP(2)
RETURN
13. MODEM
INPAUX$
With the command INPAUX$ the internal RS232-buffer is read and at the same
time cleared. You can find the address of the input-buffer with :
adr.in%=LPEEK(XBIOS(14,0))
The output-buffer can be located with :
adr.out%=LPEEK(XBIOS(14,0)+14)
INP
If you use INP(1) to read incoming bytes, you should always check with
INP?(1) if the RS232-buffer contains data.
Rsconf (XBIOS 15)
With XBIOS 15 (Rsconf) you can change the RS232-parameters. A few
baudrates :
0 - 19200 baud
1 - 9600 baud
4 - 2400 baud
7 - 1200 baud
9 - 300 baud
14 - 75 baud
Use -1 for parameters you don't want to change. Due to a TOS-bug, you
can't use 75 baud, because '14' results in 120 baud. Also, the old TOS
(pre-Blitter age) can't handle hardware handshake with RTS/CTS-signals.
Atari has released a bug-fix that should enable any TOS to use RTS/CTS.
Software handshaking (XON/XOFF) functions properly. The default
after power-up is no handshake protocol.
14. MOUSE
Editor
Sometimes the editor seems to freeze while the cursor is blinking rapidly.
Just move the mouse and the editor comes alive again.
Fileselector
Sometimes the same blinking mouse appears after calling the Fileselector
or an Alert-box. I've read somewhere this could be due to the combination
of a VDI-function (for the mouse) and an AES-function (for Fileselector or
Alert-box). GFA takes care of the first and GEM of the second, and
sometimes this seems to result in a conflict.
MOUSE
If you repeatedly call a Procedure in which you test for a mouse-click,
you should incorporate a short pause in the Procedure (e.g. PAUSE 5).
Otherwise the Procedure might be called again while the user is still
holding the button down. You could also wait until the user releases the
button :
REPEAT
UNTIL MOUSEK=0 ! wait until button is released
If you find it difficult to move the mouse accurately, you could use
<Shift> <Alternate> <arrow> to move the mouse-cursor one pixel in the
desired direction. If you press <Insert> as well, you can "drag" something
accurately as if the left mouse-button was pressed.
You can find the maximal x- and y-coordinates of the mouse with :
x=DPEEK(&H9862)
y=DPEEK(&H9864)
But there's no guaranty you will find the coordinates there. Experiment
with XBIOS 0 for a more reliable method. Don't ask me how.
MOUSE returns negative coordinates if the mouse is to the left of or above
the current window (or the origin that has been selected with CLIP
OFFSET).
SETMOUSE
A mouseclick can be simulated with :
SETMOUSE mx,my,mk
If you try this with an Alert-button you have to move the mouse after-
wards, or the button won't be selected. I don't know why, so I can't tell
if this is a bug.
DEFMOUSE
You can use one of the many Public Domain mouse-editors to design your own
mouse-mutant. But it's also easy to create a new mouse-cursor with the
Procedure Initio.mouse1 :
PROCEDURE initio.mouse1
' *** global : MOUSE1$
RESTORE pattern.mouse1
@make.mouse(mouse1$)
pattern.mouse1:
' *** x,y,mode(0=normal;1=XOR),mask-colour,mouse-colour
DATA 0,0,0,0,1
' *** mask-pattern (1 = pixel on , 0 = pixel off)
DATA 0000000000000000
DATA 0000000000000000
DATA 0000000000000000
DATA 0000000000000000
DATA 0000000000000000
DATA 0000000000000000
DATA 0000000000000000
DATA 0000000000000000
DATA 0000000000000000
DATA 0000000000000000
DATA 0000000000000000
DATA 0000000000000000
DATA 0000000000000000
DATA 0000000000000000
DATA 0000000000000000
DATA 0000000000000000
' *** mouse-pattern
DATA 0000000000000000
DATA 0000000000000000
DATA 0000000000000000
DATA 0000000000000000
DATA 0000000000000000
DATA 0000000000000000
DATA 0000000000000000
DATA 0000000000000000
DATA 0000000000000000
DATA 0000000000000000
DATA 0000000000000000
DATA 0000000000000000
DATA 0000000000000000
DATA 0000000000000000
DATA 0000000000000000
DATA 0000000000000000
RETURN
'
PROCEDURE make.mouse(VAR m$)
LOCAL x,y,mode,msk.color,mouse.color,n,msk%,mouse%,msk.pat$
LOCAL mouse.pat$,msk$,mouse$,pat$
CLR msk.pat$,mouse.pat$,pat$
READ x,y,mode,msk.color,mouse.color
FOR n=1 TO 16
READ msk$
msk%=VAL("&X"+msk$)
msk.pat$=msk.pat$+MKI$(msk%)
NEXT n
FOR n=1 TO 16
READ mouse$
LET mouse%=VAL("&X"+mouse$)
LET mouse.pat$=mouse.pat$+MKI$(mouse%)
NEXT n
m$=MKI$(x)+MKI$(y)+MKI$(mode)+MKI$(color.index(msk.color))
m$=m$+MKI$(color.index(mouse.color))+msk.pat$+mouse.pat$
RETURN
The mask should be an exact copy of the mouse-pattern if you need a
transparant mouse. Leave the mask empty (all '0') and the mouse will
disappear behind objects on the screen. Fill the mask with '1' and the
16x16 mouse will always remain visible. Switch mask- and mouse-colour in
the first DATA-line to create a "reverse" mouse. Or use any VDI colour-
index that is available in the current resolution.
15. JOYSTICK
STRIG and STICK
Here is an example of the use of STRIG and STICK if your joystick is
connected to Port 1 (your mouse is connected to Port 0) :
STICK 1 ! joystick-mode (your mouse is now dead)
REPEAT
IF STRIG(1)
joy=3
ELSE
joy=STICK(1)
ENDIF
ON joy GOSUB n,s,fire,w,nw,sw,dummy,e,ne,se
PAUSE 5
(...)
UNTIL condition!
STICK 0 ! back to mouse-mode
You need eight Procedures for the eight possible directions (see below)
and one Procedure for the Fire-button. The value 7 ('dummy') never occurs.
If you don't touch the joystick, the value of 'joy' will be 0, and this
means no Procedure will be called. A short pause is advisable, because
GFA-Basic is too fast.
5 1 9
\|/
directions : 4-0-8
/|\
6 2 10
If you desperately need an active mouse while using the joystick, you
could try the following "dirty" method :
REPEAT
IF MOUSEK=2
joy=3
ELSE
joy=BYTE{&HE09} ! for TOS-version 1.0 !!
ENDIF
ON joy GOSUB n,s,fire,w,nw,sw,dummy,e,ne,se
PAUSE 5
UNTIL condition!
16. SOUND
SOUND and WAVE
After a SOUND-command, the sound sometimes continues in spite of the
elapsed time. If the command is followed by another SOUND- or a WAVE-
command, the time is handled correctly. This certainly sounds like a GFA-
bug.
The easiest way to stop all sound is :
WAVE 0,0 ! turn all sound off
Sound of a certain frequency can be produced with :
SOUND ch,vol,#ROUND(125000/freq%),pause
Although our ST is not famous for its brilliant sound, you can produce
nice sound-effects with the simple commands SOUND and WAVE. Check out the
Procedures Siren.sound, Tideli.sound, Bounce1.sound and Bounce2.sound to
hear what I mean.
Dosound (XBIOS 32)
XBIOS 32 (Dosound) can be used to play music in a special format. I
propose to use the extension 'X32' for song-files in this format. The
operating system takes care of playing the music during interrupts (every
1/50th second). Take a look at the Procedures Play.song and Stop.song to
see how you could use XBIOS 32 in your programs. You can even play a song
continuously with the Procedure Play.cont.song. Temporarily stopping a
song is possible with the Procedure Interrupt.song.
If you use XBIOS 32 to play music, you are advised to switch the key-click
off. Otherwise the music will stop as soon as the user presses a key.
Once I discovered XBIOS 32 did not work during the initialization (mainly
the filling of arrays) of a large program. I had converted the program
from GFA-Basic 2.0 to 3.0, but I did not have the patience to find out
what caused this problem.
XBIOS 32 can also be used for sound-effects. I have developed the
Procedure Initio.sound for building sound-strings from a few DATA-lines.
I hereby declare this as the standard method for creating sound-strings.
After @do.sound(sound$) you can hear the sound-effect. In the following
example the sound-string bounce3$ is created :
PROCEDURE initio.sound
' *** commands in DATA-lines :
' *** REG = 14 parameters for registers 0-13
' *** END = end of sound-string
' *** PAUSE = pause (followed by time in 1/50 seconds)
' *** VAR = decrease/increase tone : channel,start,+/-step,
' *** end-value
'
bounce3.sound:
DATA REG,0,0,0,0,0,0,27,248,16,16,16,35,95,0
DATA VAR,3,255,-1,116
DATA PAUSE,255,END
RESTORE bounce3.sound
@sound.string(bounce3$)
RETURN
'
PROCEDURE sound.string(VAR s$)
LOCAL n,snd$,snd,channel,begin,step,end
s$=""
DO
READ snd$
snd$=UPPER$(snd$)
EXIT IF snd$="END"
IF snd$="REG"
FOR n=0 TO 13
READ snd
s$=s$+CHR$(n)+CHR$(snd)
NEXT n
ENDIF
IF snd$="PAUSE"
READ snd
s$=s$+CHR$(130)+CHR$(snd)
ENDIF
IF snd$="VAR"
READ channel,begin,step,end
s$=s$+CHR$(128)+CHR$(begin)+CHR$(129)+CHR$(channel)+CHR$(step)
s$=s$+CHR$(end)
ENDIF
LOOP
s$=s$+CHR$(255)+CHR$(0) ! terminator
RETURN
'
PROCEDURE do.sound(sound$)
VOID XBIOS(32,L:VARPTR(sound$))
RETURN
Samples
From GFA-Basic you can surprise the user with a sampled sound. Examine the
Procedures Sample, Load.sample and Play.sample to see how. You'll have to
find suitable samples first. Look out for sound-effects and speech-
samples. Personally, I just love the famous Perfect-sample.
Speech
Your ST can talk to you with a little help (STSPEECH.TOS). My current GFA-
version (3.07) refuses to cooperate with the Procedures Initio.speech and
Talk, but I have included these anyway. Perhaps you can discover the bug.
In earlier versions both Procedures did work. A reset is necessary because
EXEC 3 is used.
Soundmachine
You can play songs, created with Soundmachine (TommySoftware), from GFA-
Basic. You'll need the Procedures Initio.soundmachine and Soundmachine,
and a song-file. You'll probably wonder if it's really your ST that's
playing the song. Soundmachine II is out, and looks even better. In this
new version you can choose between playing songs with samples, or without
samples (or samples for one of the three channels). You'll need the
Procedure Sm.initio or Msm.initio and several others. Try to find the
Public Domain demo-programs SAMSOUND.GFA, CHIPSND.GFA and SND_PLAY.GFA.
End of commercial, continue with text.
17. PROGRAM DECISIONS
IF ... ENDIF
If the value of a certain variable must fall in the range min%-max%, you
could program that as follows :
IF n%>max%
n%=max%
ENDIF
IF n%<min%
n%=min%
ENDIF
In this case you could also use MAX and MIN :
n%=MAX(MIN(n%,max%),min%)
You probably test for two conditions by using :
IF cond1! AND cond2!
(...) ! both true
ENDIF
But separate testing is much faster :
IF cond1!
IF cond2!
(...) ! both true
ENDIF
ENDIF
SELECT
Multiple 'ELSE IF'-constructions can be replaced by a 'SELECT'-
construction. At each CASE you can use integers, strings or integer-
variables (not string-variables). Only the first four bytes of a string
can be used. The editor will not accept something like CASE "test2",
only CASE "test".
18. PROGRAM LOOPS
Calculations
Try to remove all unnecessary calculations from loops, e.g. :
FOR i=1 TO 1000
x(i)=2*q*i
NEXT i
It's a better idea to calculate 2*q outside the loop :
q2=q*2
FOR i=1 TO 1000
x(i)=q2*i
NEXT i
Always try to convert floating point variables to integers before entering
a loop. Then you will be able to use the fast integer-operators. An
obvious example would be a calculation with dollars (24.37) that could be
replaced by a calculation with cents (2437). Use this method if you know
the lowest possible value of the floating point variable (0.01 in this
case) and then divide by this value. But watch out for rounding errors and
integer-overflow.
Powers of 2 can be calculated fast by setting a bit :
x%=BSET(0,6) ! faster than x%=2^6
And for the ultimate speed-freaks, multiplying with a power of 2 is
slightly faster with SHL :
y%=SHL(x%,3) ! faster than y%=MUL(x%,8)
Of course there is no overflow-control if you use SHL (or MUL).
Sometimes, calculations in a loop can be replaced by a look-up table :
FOR i=1 TO 1000
y%(i)=x|(i)^2
NEXT i
First, create a table of squares :
DIM square%(255)
FOR i=0 TO 255
square%(i)=i*i
NEXT i
Then use this table in the loop :
FOR i=1 TO 1000
y%(i)=square%(x|(i))
NEXT i
FOR ... NEXT
I use local variables in Procedures if possible. But if you intend to
compile the program later, you should declare the counter in a FOR ...
NEXT loop as a global variable. In the compiled program, the loop will be
executed faster!
If you use floating point count-variables in a FOR ... NEXT loop (or any
other loop), you could encounter unexpected problems :
FOR i#=0.1 TO 0.9 STEP 0.1
PRINT i#
NEXT i#
You would expect 0.9 as the result of the last addition (0.8 + 0.1), but
0.9 is never printed! This is not a bug in GFA-Basic, but caused by the
internal (binary) representation of floating point numbers. The last
addition results in a number slightly larger than 0.9 and therefore the
loop is left after printing 0.8 . If you insert the line
i#=ROUND(i#,14)
in the loop, you can solve this problem. But the best solution is to avoid
floating point count-variables in loops. Integer count-variables are much
faster. You could easily change the loop to :
FOR i=1 TO 9
PRINT USING "#.#";i/10
NEXT i
One more time: 'i' is a word-variable, because I use word-variables as
the default for number-variables without postfix.
If you use a floating point variable in a REPEAT UNTIL loop, you can avoid
the rounding error by using the special operator '==' :
i#=0
REPEAT
i#=i#+0.1
PRINT i#
UNTIL i#==0.9
But you can't use the '=='-operator in a FOR ... NEXT loop.
In the following two examples a byte-variable is used as the counter :
FOR i|=5 DOWNTO -1
PRINT i|
NEXT i|
'
FOR i|=250 TO 256
PRINT i|
NEXT i|
Of course, a byte-variable cannot have a value of -1 or 256. GFA does not
abort with an error-message, but skips the loops. Not a true bug perhaps,
but close.
Loops
Although you can use many different loops in GFA-Basic 3.0, there are
basically only two varieties. You can first test a condition, and then
either continue or leave the loop. Or you can first enter the loop, and
then test a condition to decide if you are going to continue or leave.
In an interpreted program the first choice is the fast FOR ... NEXT loop,
then the (slower) REPEAT ... UNTIL loop and finally the (slowest) WHILE
... WEND loop. In a compiled program all loops are executed equally fast!
If you use a DO ... LOOP, an EXIT IF condition will always take some extra
time.
Try to avoid a negative test in a loop, as this will take more time. E.g.
replace :
WHILE NOT condition!
(...)
WEND
by the much faster :
DO UNTIL condition!
(...)
LOOP
Or similarly replace :
REPEAT
(...)
UNTIL NOT condition!
by the faster :
DO
(...)
LOOP WHILE condition!
Finally, you could combine the test of one condition at the start of the
loop with the test of another condition at the end of the loop :
DO UNTIL condition_1!
(...)
LOOP WHILE condition_2!
19. PROGRAM CONTROL
GOSUB
A Procedure can be called in one of the following ways :
GOSUB proc
@proc
proc
I prefer the second method, because this is spotted easily in a listing.
Also, the same method can be used to call a function.
ON BREAK GOSUB
You can't stop a program if a recursively called function is executed (?).
Use ON BREAK CONT to prevent calling the Break-Procedure twice. Nobody can
release the <Control> <Shift> <Alternate> keys fast enough :
ON BREAK GOSUB break
(...)
PROCEDURE break
ON BREAK CONT
(...)
ON BREAK GOSUB break
RETURN
In this case the Break-Procedure is activated again before leaving the
Procedure. Study the Standard Procedure Break (in one of the STANxxxx.LST-
files) for an example of this method.
ERROR
You can simulate ERRORs with values from -127 to 127. For GFA-errors use
values from 0 to 93, for bomb-errors 102 (2 bombs) to 109 (9 bombs) and
for TOS-errors -1 to -67.
EVERY and AFTER
It's not possible to use EVERY and AFTER at the same time. Both commands
don't work during a long PAUSE or DELAY (or any other command that takes a
lot of time). You can only call Procedures without parameters. Don't make
the Procedure too long, or it may be called while being processed!
In a compiled program you have to incorporate '$I+ U+', or EVERY and AFTER
can't be used.
GOTO
You can't use GOTO in a Procedure, a Function or a FOR ... NEXT loop.
DELAY
The DELAY-command does not operate correctly in version 3.07 of GFA-Basic.
During DELAY a Break is impossible. A nastier bug is the appearance of the
mouse-cursor during DELAY, even after HIDEM. You are advised to use PAUSE
instead.
CHAIN
In GFA-Basic all variables and arrays are lost after CHAINing the next
program. However, you could use the 160-byte buffer of the scrap-library
to pass a short message to the next program :
buffer$=SPACE$(160) ! 160 bytes maximum ??
message$="this message was sent by the previous program"
message$=message$+CHR$(0)
LSET buffer$=message$
r%=SCRP_WRITE(buffer$) ! r%=0 if error
CHAIN file$
Read the message with :
buffer$=SPACE$(160)
r%=SCRP_READ(buffer$)
message$=CHAR{V:buffer$}
The use of this buffer is completely illegal, but who cares if you don't
use a scrap-library? Unfortunately the GFA-editor seems to think so too,
so you should experiment a little before trusting this method.
EXEC
If you are going to run another program more than once, you'll have to use
EXEC 3 :
base%=EXEC(3,file$,"","") ! load, but don't start yet
base$=STR$(base%)
cmdl$=CHR$(LEN(base$)+1)+base$ ! create command line
(...)
r%=EXEC(4,"",cmdl$,"") ! now run it
The variable r% contains a value returned by the program (or -39 if not
enough memory was available). Repeat the last line if you want to run the
loaded program again. Of course you should use EXEC 0 if you're going to
run the program one time only. Read the paragraph 'RESERVE' if you are
going to use EXEC 3.
If you call a '*.PRG'-program with EXEC 0, you pass the null-string ("")
as the command-line. You need the command-line only if you call a '*.TTP'-
program. The command-line is converted to upper case and can't exceed 125
bytes. The first byte of the command-line (usually) determines the length
of the line, so the command-line can't contain more than 124 characters.
You can use this in a TTP-program (compiled GFA-Basic program, extension
changed to TTP), but it's easier to read the command-line with :
cmdl$=CHAR{BASEPAGE+&H81}
20. GRAPHICS
SETCOLOR and VSETCOLOR
With commands like COLOR, DEFTEXT, DEFFILL, etc., you use a VDI colour-
index. Unfortunately SETCOLOR uses a different table :
VDI colour-index : 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
SETCOLOR (Low rez) : 0 15 1 2 4 6 3 5 7 8 9 10 12 14 11 13
SETCOLOR (Med rez) : 0 3 1 2
From this table it follows you would have to use 'SETCOLOR 2,r,g,b' to
change colour-index 3. By the way, all Line-A commands use the SETCOLOR-
table as well! This is not a "bug", but a consequence of two different
colour-tables that are used by GEMDOS and GEM. You are advised to use :
VSETCOLOR index,r,g,b
This index is exactly the same as the colour-index, so you would use
'VSETCOLOR 2,r,g,b' in order to change colour-index 2. If you like, you
could use the hexadecimal 3-byte mix-value :
VSETCOLOR index,&Hrgb
But you won't like it at all, because this doesn't work properly. GFA-
Basic swaps the r- and g-byte, so you would have to use :
VSETCOLOR index,&Hbgr
Better avoid this method, or you'll experience colour-changes if GFA
corrects this bug in a future version.
In High resolution you can invert the screen-colours with :
VSETCOLOR 0,0 ! reverse High-screen (black background)
VSETCOLOR 1,0 ! normal High-screen (black letters)
An inverted screen is perhaps slightly less suitable for text, but
graphics look superb.
Colour-index 0 determines the colour of the background in Low and Medium
resolution. This index also determines the colour of the border on your
screen. You can change this colour, although you can't draw or PRINT on
the screen-border of your colour monitor. The colour of all PRINTed text
is determined by colour-index 1, unless you use the 'Esc b' command (read
the paragraph 'PRINT' again if your long-term memory is too short) :
VSETCOLOR 0,r,g,b ! change colour of background
VSETCOLOR 1,r,g,b ! change colour of all PRINTed text
Palette
Before changing colours, you should always save the current palette. And
do restore the old palette before the user exits the program. I hate
programs that return to a pink or yellow desktop. You can store the
palette either in an integer array or in a string, using XBIOS 7
(Setcolor) :
PROCEDURE save.palette
LOCAL i
ERASE old.palette%()
DIM old.palette%(15)
FOR i=0 TO 15
old.palette%(i)=XBIOS(7,i,-1)
NEXT i
RETURN
'
PROCEDURE make.palette.string(VAR pal$)
LOCAL n
pal$=""
FOR n=0 TO 15
pal$=pal$+MKI$(XBIOS(7,n,-1))
NEXT n
RETURN
The string-method is compatible with Degas. Restore the old palette with
the corresponding Procedures :
PROCEDURE restore.palette
LOCAL i
FOR i=0 TO 15
VOID XBIOS(7,i,old.palette%(i))
NEXT i
RETURN
'
PROCEDURE change.palette(pal.string$)
VOID XBIOS(6,L:VARPTR(pal.string$))
RETURN
You could also use the Standard Procedure Standard.low.colors or
Standard.med.colors to restore the default palette. You can find these
Procedures in the STANxxxx.LST-files.
You can examine the rgb-value of a certain VDI colour-index with :
PROCEDURE rgb.value(index,VAR rgb$)
LOCAL col%
col%=XBIOS(7,color.index(index),-1)
rgb$=RIGHT$(HEX$(col%),3)
RETURN
The Standard Array color.index() is used to convert the VDI colour-index.
A completely new palette for Medium resolution can be installed with :
PROCEDURE new.med.colors
LOCAL n,r,g,b,col$
RESTORE med.new.col.data
FOR n=0 TO 3
READ col$
r=VAL(LEFT$(col$))
g=VAL(MID$(col$,2,1))
b=VAL(RIGHT$(col$))
VSETCOLOR n,r,g,b
NEXT n
med.new.col.data
DATA 000,000,000,000
RETURN
You can show the current palette on the screen with the Procedure
Palette.box :
PROCEDURE palette.box(x,y,h,w)
' *** left upper corner of rectangle at x,y
' *** rectangle-height h; width of one colour-box w
LOCAL arect.fill,fill.adr%,i,x1,x2
x2=x+16*w+2
COLOR black
BOX x,y,x2,y+h
arect.fill=-1
f%=V:arect.fill
IF low.res!
FOR i=0 TO 15
x1=ADD(SUCC(x),MUL(i,w))
ARECT x1,SUCC(y),ADD(x1,w),PRED(ADD(y,h)),color.index(i),0,f%,0
NEXT i
ELSE IF med.res!
FOR i=0 TO 3
x1=ADD(SUCC(x),MUL(i,w))
ARECT x1,SUCC(y),ADD(x1,w),PRED(ADD(y,h)),color.index(i),0,f%,0
NEXT i
ENDIF
RETURN
You can darken the screen by dimming all colours simultaneously with the
Procedure Dim.colors :
PROCEDURE dim.colors(reg1,reg2,val)
' *** dim colours from VDI colour-index reg1 to reg2 with val
' *** for val=1 colour 254 (rgb) will become 143
LOCAL i,r,g,b
FOR i=reg1 TO reg2
@rgb.value(i,rgb$)
r=MAX(PRED(VAL(LEFT$(rgb$))),0)
g=MAX(PRED(VAL(MID$(rgb$,2,1))),0)
b=MAX(PRED(VAL(RIGHT$(rgb$))),0)
VSETCOLOR i,r,g,b
NEXT i
RETURN
Nothing is impossible in GFA-Basic, even colour-cycling is easy with
EVERY in the Procedure Color.cycle :
PROCEDURE color.cycle(reg1,reg2,time)
' *** cycles colours from VDI colour-index reg1 to reg2
' *** global : COLOR.CYCLE! COL.REG1 COL.REG2
IF NOT color.cycle!
col.reg1=reg1
col.reg2=reg2
color.cycle!=TRUE
EVERY time GOSUB cycle.once
ELSE
color.cycle!=FALSE
EVERY STOP
ENDIF
RETURN
'
PROCEDURE cycle.once
LOCAL col1%,col2%
col1%=XBIOS(7,color.index(col.reg2),-1)
FOR reg=col.reg1 TO PRED(col.reg2)
col2%=XBIOS(7,color.index(reg),-1)
~XBIOS(7,color.index(reg),col1%)
SWAP col1%,col2%
NEXT reg
~XBIOS(7,color.index(col.reg2),col1%)
RETURN
Call the Procedure Color.cycle again to stop the colour-cycling.
DEFMARK
The point as mark-symbol (No. 1) cannot be enlarged. Other mark-symbols
can be enlarged, but all lines in the symbol retain a width of one pixel.
The size of a symbol must be a multiple of 11 plus 6: 0, 17, 28, 39, 50.
For a value in between, the prior allowed size is chosen. Perhaps there is
some magic hidden in the allowed sizes, or am I missing something?
DEFFILL
If you haven't defined your own Fill-pattern, the Atari-symbol will be
used after DEFFILL 1,4,x. The desktop Fill-pattern is DEFFILL 1,2,4.
The Fill-pattern string can consist of 16, 32 or 64 words (MKI$-format).
Word 1 to 16 is needed for bitplane 0 (High resolution), word 17 to 32 for
bitplane 1 (Medium resolution) and word 33 to 64 for the bitplanes 2 and 3
(Low resolution). In all resolutions, the pattern occupies a rectangle of
16x16 pixels on the screen. The same 16x16 rectangle is also used for the
mouse-cursor and sprites. You can always use a Fill-pattern in a lower
resolution, e.g. a High-pattern in Medium or Low resolution, but not the
other way around. Examine the three Procedures Initio.high.fill1,
Initio.med.fill1 and Initio.low.fill1 to see how easy you can design your
own Fill-patterns. There's gold in them thar Fills. You could surprise the
user with a bomb-pattern :
PROCEDURE initio.bomb.fill
' *** global : BOMB.FILL$
RESTORE bomb.fill
@make.high.fill(bomb.fill$)
bomb.fill:
DATA 0000011000000000
DATA 0010100100000000
DATA 0000000010000000
DATA 0100100001000000
DATA 0001000111110000
DATA 0000000111110000
DATA 0000011111111100
DATA 0000111111111110
DATA 0000110111111110
DATA 0001111111111111
DATA 0001111111101111
DATA 0000111111101110
DATA 0000111111011110
DATA 0000011111111100
DATA 0000001111111000
DATA 0000000011100000
RETURN
'
PROCEDURE make.high.fill(VAR fill$)
LOCAL i,pat$,pat%
CLR fill$
FOR i=1 TO 16
READ pat$
pat%=VAL("&X"+pat$)
fill$=fill$+MKI$(pat%)
NEXT i
RETURN
Be careful, a screen filled with this pattern might provoke a heart-
attack. If it doesn't, you could try the following dirty trick (High
resolution only) with the Procedures Busy and Achtung :
@busy ! looks like the computer is very busy
~INP(2) ! but nothing happens, until the user presses a key...
@achtung
PAUSE 500
CLS
PRINT " Thank you for your patience, I'm not busy anymore ..."
PAUSE 150
Many Fill-patterns are available as files. You can use these with
something like Procedure Initio.fill1 :
PROCEDURE initio.fill1(VAR pattern$)
LOCAL bytes
bytes=32 ! 32 bytes for High resolution
' *** load Fill-pattern (32 bytes for High resolution) here
INLINE fill1%,32
pattern$=STRING$(bytes,0)
BMOVE fill1%,V:pattern$,bytes
DEFFILL ,pattern$
RETURN
FILLing a screen with a pattern takes some time, especially in High
resolution. Use the following method for almost immediate filling of the
entire High resolution screen :
PROCEDURE full.fill(fill%)
ACLIP 1,0,0,639,399
ARECT 0,0,639,399,1,0,fill%,15
ACLIP 0,0,0,639,399
RETURN
Fill% is the address of a FILL-pattern (32 bytes). This very fast
alternative Fill-method works in High resolution only.
DEFLINE
After 'DEFLINE ,n' all horizontal lines have a width of n pixels (n should
be odd), except in Medium resolution. But if n is larger than 1, all
vertical lines are n+2 pixels wide! I have trouble counting the pixels
in Medium resolution. I think the width of horizontal lines is :
n : 1 3 5 7 9
width : 1 1 3 3 5 etc.
Is this documented anywhere?
You define your own Line-patterns by using a negative 16-bit value (from
-&X1 to -&X1111111111111111). Each set bit corresponds with a pixel in the
Line-pattern (High resolution). The highest bit (15) corresponds with the
leftmost pixel of the pattern. Don't get confused if the editor changes
the negative binary number you entered as a pattern. The editor uses a
special notation for negative binary numbers.
GFA-Basic represents integer numbers as binary strings of 32 bits. The
most significant bit (31) determines the sign of the integer. If this bit
is 0, the remaining 31 bits represent an "ordinary" positive number. But
if the most significant bit is 1, the remainder is a negative number in
'two's complement' notation. I won't try to explain that. In any case, the
next time you type -&X111 and a friend is watching you, don't blink your
eyes, but casually remark "of course the editor converts this into two's
complement notation". That's also the reason GFA-Basic can work with
integers from -(2^31) to +(2^31)-1.
DEFTEXT
In some publications you can read about 'shadowed text' (style = 32), but
unfortunately our GEM doesn't know this style. You can even find the mask
for shadowed text with WORD{L~A+92}, but it's 0.
I never succeeded in finding the current text-style with VDISYS 38
(vqt_attributes). Either a bug in GEM or my mistake. I don't want to know,
because the easy way is :
txt.style=WORD{L~A+90}
This could be important if you intend to use DEFTEXT in a Procedure, but
would like to restore the original DEFTEXT-settings before leaving the
Procedure.
With DEFTEXT you can set the height of TEXT-letters. A letter occupies
more space though, determined by the letter-box :
letter-height letter-box system-font
4 6 x 6 icon
6 8 x 8 Medium & Low rez
13 8 x 16 High resolution
The letter-height is the distance from Descent-Line to Ascent-Line, but
the Bottom-Line and Top-Line lie at least one pixel lower/higher. The
height of the letter-box is the distance from Bottom-Line to Top-Line.
Most letters rest on the Base-Line, but letters with a descender (g,j,p,
etc.) rest on the Descent-Line. If you are still with me, from top to
bottom we have the following lines :
Top-Line
Ascent-Line
Base-Line
Descent-Line
Bottom-Line
Drop me a line if you don't understand this.
GRAPHMODE
If you draw a rectangle with BOX in GRAPHMODE 3 (Xor-mode), the pixel in
the left upper corner is not drawn. Actually this pixel is drawn twice,
and in GRAPHMODE 3 this means the pixel disappears. Use PLOT to draw this
pixel :
GRAPHMODE 3
BOX x1,y1,x2,y2
PLOT x1,y1 ! and fill the gap
With PBOX in GRAPHMODE 3 you'll also get trouble in the same corner. Avoid
this by using the command 'BOUNDARY 0' first :
GRAPHMODE 3
BOUNDARY 0
PBOX 50,50,100,100
GRAPHMODE 3 is especially useful if you make a temporary drawing. Draw the
same picture a second time to restore the original screen. Examine Proce-
dures like Rubber.line, Draw.box and Drag.box for examples of this method.
The Procedure Rubber.line is used to draw a line from (x,y) to the mouse-
cursor :
PROCEDURE rubber.line(x,y,VAR x2,y2)
LOCAL x1,y1,x2,y2,k
GRAPHMODE 3
DEFMOUSE 3
SHOWM
MOUSE x1,y1,k
REPEAT ! main loop
LINE x,y,x1,y1 ! draw line
REPEAT
MOUSE x2,y2,k
UNTIL x2<>x1 OR y2<>y1 OR k>0 ! mouse moved
LINE x,y,x1,y1 ! undraw line
x1=x2
y1=y2
UNTIL k>0 ! mouse-click : ready (x2 and y2 returned)
GRAPHMODE 1
LINE x,y,x2,y2 ! this is it
HIDEM
DEFMOUSE 0
PAUSE 10
RETURN
You could also use the 'GRAPHMODE 3'-method for animation, but the XBIOS 5
(Setscreen) method is more suitable.
Don't try to draw in GRAPHMODE 3 with a linewidth greater than 1 pixel.
GEM will surprise you with some modern art if you can't resist the
temptation.
If you want to confirm a particular choice of the user, you can invert the
relevant part of the screen with the Procedure Invert.block. Call this
Procedure again with the same parameters to restore the original screen :
PROCEDURE invert.block(x1,y1,x2,y2,color)
GRAPHMODE 3
DEFFILL color,1
BOUNDARY 0
PBOX x1,y1,x2,y2
BOUNDARY 1
GRAPHMODE 1
RETURN
You can 'grey out' an unavailable option on the screen with :
PROCEDURE block.dimmer(x1,y1,x2,y2,color)
GRAPHMODE 3
DEFFILL color,2,2
BOUNDARY 0
PBOX x1,y1,x2,y2
BOUNDARY 1
GRAPHMODE 1
RETURN
Call this Procedure again to restore the screen.
The entire (High resolution) screen can be dimmed with :
PROCEDURE screen.dimmer
' *** global : DIMMER.SCREEN$ DIMMER.SWITCH!
IF dimmer.switch!
SPUT dimmer.screen$
dimmer.switch!=FALSE
ELSE
SGET dimmer.screen$
GRAPHMODE 4
DEFFILL 1,2,4
PBOX 0,0,639,399
dimmer.switch!=TRUE
ENDIF
RETURN
The screen will be restored if you call the Procedure again.
PLOT and DRAW
You can use both 'PLOT x,y' and 'DRAW x,y' to set a point on the screen.
The size of the point can be changed :
DEFLINE ,size,2,2 ! change size of points
But the shapes you'll see don't look like points anymore, due to the same
problem as described in the paragraph 'DEFLINE'. Use PCIRCLE for proper
fat points.
PCIRCLE
With CLIP on, a PCIRCLE touching the upper screen-border is not filled
properly in High resolution :
CLIP 0,0,640,400
PCIRCLE 0,0,50
I don't know if we should blame GFA or GEM for not filling the two top-
lines in the circle.
CURVE
With the command CURVE you can draw a Bezier-curve :
CURVE x1,y1,x2,y2,x3,y3,x4,y4
The Bezier-curve starts at (x1,y1) and ends at (x4,y4). The other two
points act like little magnets. You can also use this command to draw a
'normal' curve between two points by letting the points (x3,y3) and
(x4,y4) coincide. Try the following to see what I mean :
GRAPHMODE 3
MOUSE x2,y2,k
DO
CURVE 10,100,x2,y2,110,100,110,100 ! draw curve
REPEAT
MOUSE x,y,k
UNTIL x<>x2 OR y<>y2
CURVE 10,100,x2,y2,110,100,110,100 ! erase curve
x2=x
y2=y
LOOP
This method could be used to draw large letters.
TEXT
The coordinates used with TEXT determine the start of the Base-Line of the
text. The descenders of letters like 'g', 'j', and 'p' lie below the Base-
Line. This is especially important, not to say frustrating, if you use
TEXT with an angle of 90, 180 or 270 degrees. The text rotates anticlock-
wise around the TEXT-coordinates!
If you intend to combine PRINTed text with TEXT, you probably will like
the Procedure Text.at as the analogue of 'PRINT AT' :
PROCEDURE text.at(c,l,t$)
TEXT (c-1)*char.width,l*char.height+3*high.res!+2*(NOT high.res!),t$
RETURN
You can use TEXT to print "digital" numbers (ASCII-code 16-25) :
FUNCTION digital$(number$)
LOCAL dig$,i
CLR dig$
FOR i=1 TO LEN(number$)
dig$=dig$+CHR$(BCLR(ASC(MID$(number$,i,1)),5))
NEXT i
RETURN dig$
ENDFUNC
Use this Function as follows :
TEXT x,y,@digital$("1237")
SPRITE
You could design and save sprites in vitro with one of the many Sprite-
editors that are available. Or you can use something like the Procedure
Initio.sprite1 to create a sprite in vivo. Compare this Procedure also
with the Procedure Initio.mouse1 :
PROCEDURE initio.sprite1
' *** global : SPRITE1$
RESTORE pattern.sprite1
@make.sprite(sprite1$)
pattern.sprite1:
' *** x,y,mode(0=normal;1=XOR),mask-colour,sprite-colour
DATA 0,0,0,0,1
' *** mask-pattern (1 = pixel on , 0 = pixel off)
DATA 0000000000000000
DATA 0000000000000000
DATA 0000000000000000
DATA 0000000000000000
DATA 0000000000000000
DATA 0000000000000000
DATA 0000000000000000
DATA 0000000000000000
DATA 0000000000000000
DATA 0000000000000000
DATA 0000000000000000
DATA 0000000000000000
DATA 0000000000000000
DATA 0000000000000000
DATA 0000000000000000
DATA 0000000000000000
' *** sprite-pattern
DATA 0000000000000000
DATA 0000000000000000
DATA 0000000000000000
DATA 0000000000000000
DATA 0000000000000000
DATA 0000000000000000
DATA 0000000000000000
DATA 0000000000000000
DATA 0000000000000000
DATA 0000000000000000
DATA 0000000000000000
DATA 0000000000000000
DATA 0000000000000000
DATA 0000000000000000
DATA 0000000000000000
DATA 0000000000000000
RETURN
'
PROCEDURE make.sprite(VAR s$)
LOCAL x,y,mode,msk.color,spr.color,n,msk%,spr%,msk.pat$,spr.pat$
LOCAL msk$,spr$,pat$
CLR msk.pat$,spr.pat$,pat$
READ x,y,mode,msk.color,spr.color
FOR n=1 TO 16
READ msk$
msk%=VAL("&X"+msk$)
msk.pat$=msk.pat$+MKI$(msk%)
NEXT n
FOR n=1 TO 16
READ spr$
spr%=VAL("&X"+spr$)
spr.pat$=spr.pat$+MKI$(spr%)
NEXT n
FOR n=1 TO 16
pat$=pat$+MID$(msk.pat$,n*2-1,2)+MID$(spr.pat$,n*2-1,2)
NEXT n
s$=MKI$(x)+MKI$(y)+MKI$(mode)+MKI$(color.index(msk.color))
s$=s$+MKI$(color.index(spr.color))+pat$
RETURN
The mask should be an exact copy of the sprite-pattern if you need a
transparant sprite. Leave the mask empty (all '0') and the sprite will
disappear behind objects on the screen. Fill the mask with '1' and the
16x16 sprite will always remain visible. Switch mask- and sprite-colour in
the first DATA-line to create a "reverse" sprite. Or use any VDI colour-
index that is available in the current resolution.
If you put a sprite on the screen, the background (16x16 pixels) is
temporarily saved. Using more than one sprite simultaneously, it is
essential you remove sprites in reverse order. This is necessary because a
sprite could overlap another sprite. Removing the top sprite first ensures
that the original background will reappear after removing the first
sprite, e.g. :
DO
(...)
SPRITE s2$ ! remove sprites...
SPRITE s1$
VSYNC ! prevents blinking, but slows down
SPRITE s1$,x1,y1 ! draw sprites...
SPRITE s2$,x2,y2
(...)
LOOP
VQT_EXTENT
The function VQT_EXTENT can be used if you would like to draw a rectangle
around a text-string :
~VQT_EXTENT(txt$,x1,y1,x2,y2,x3,y3,x4,y4)
The coordinates of the four corners depend on the angle of the text (0,
90, 180 or 270 degrees) and of course on the size of the text. The point
(x1,y1) is the lower left corner of the imaginary rectangle around the
text and the other points are arranged anticlockwise around the text-
string. Because the text rotates around the TEXT-coordinates (start of the
Base-Line), it will take some trial and error to determine the correct
position of the rectangle if the angle is not 0 degrees. In the following
table you'll find the "true" origin of the rectangle, the width and the
height of the rectangle, and also the actual position of the lower left
corner of the text-block (x1,y1) :
angle position (x1,y1) origin width height
0 lower left x1,y1 x2 y4
900 lower right x4,y4 x1 y3
1800 upper right x3,y3 x4 y2
2700 upper left x2,y2 x3 x4
The coordinates of the origin are (0,0), so with an angle of 0 degrees
both x1 and y1 are 0. The rectangle rests on the x-axis, while the left
side coincides with the y-axis. This is a "mathematical" y-axis, not a
"screen" y-axis. This means you go upwards for positive y-values. For an
angle of 0 degrees the coordinates will be :
(x4,y4). .(x3,y3)
. TEXTBLOCK .
(x1,y1) (x2,y2)
If you understand the table, you should be surprised by the height 'x4'
instead of 'y1' at an angle of 270 degrees. I think I discovered a bug in
GEM here. Correct GEM by changing the following variables if an angle of
270 degrees is used :
y1=x4
SWAP x4,y4
Now, the height is 'y1' as you suspected. Clever, aren't we? I have not
been able to confirm my discovery. None of my reference-books mention the
bug.
Line-A
The Line-A commands are faster than the corresponding VDI-commands in GFA-
Basic. The difference should be even greater after loading GDOS (?). In
the following table you can find how many times faster the Line-A command
is :
PSET / PLOT = 3
PTST / POINT = 2
HLINE / LINE = 2
ARECT / PBOX = 1.5
The syntax of Line-A commands is more complicated, but that's no problem
for us GFA-experts. Line-A commands use the 'SETCOLOR-index', so you'll
probably need the Standard Array color.index().
HLINE
An additional advantage of the commands HLINE, ARECT and APOLY is that you
don't have to change the DEFFILL-parameters in the main program. For solid
horizontal lines, use :
pattern=-1
adr%=V:pattern
HLINE x1,y,x2,color,mode,adr%,0
You can't use &X1111111111111111 (16 bits) for the pattern, because bit 15
of a word-variable is a flag for a negative number. Yes, that's why the
largest positive word is 2^15 - 1 (32767). The solution to this little
problem is to assign -1 to the word-variable. You have my word, now all 16
bits are 1. Use BIN$ if you don't believe me.
For very complicated patterns you could use a word-array :
DIM pattern(i)
adr%=V:pattern(0)
(...) ! put fill-pattern in pattern(0) to pattern(i)
HLINE x1,y,x2,color,mode,adr%,i
ACHAR and ATEXT
It's difficult to combine the Line-A commands ACHAR and ATEXT with TEXT.
The coordinates used with ACHAR and ATEXT determine the position of the
left upper corner of the (first) letter-box. That's the Top-Line, not the
Base-Line.
You can't use the text-style underlined (8) with ACHAR and ATEXT. Probably
a GFA-bug.
GET and PUT
You could save a rectangular part of the screen as follows :
GET x1,y1,x2,y2,pic$
BSAVE file$,V:pic$,LEN(pic$) ! use the extension PUT in the filename
Later, you could put the saved picture back on the screen with :
OPEN "I",#1,file$
LET bytes%=LOF(#1) ! how many bytes needed?
CLOSE #1
picture$=SPACE$(bytes%) ! reserve some space,
BLOAD file$,V:picture$ ! load the picture
PUT x,y,picture$ ! and look at it
The Procedures Message, Warning, Message.on and Message.off use GET and
PUT to save and later restore the part of the screen that is used for
text.
A GET-string starts with three words for width, height and number of
bitplanes. The number of bitplanes is determined by the resolution: 1 for
High, 2 for Medium and 4 for Low. After these three words follows the
actual picture as a list of words. As the picture-width is not necessarily
a multiple of 16, any bits beyond the right border will be ignored by the
PUT-command.
Here is the connection between GRAPHMODE and PUT-mode :
GRAPHMODE PUT-mode
1 3 (default)
2 7
3 6
4 13
I have encountered a few programs in GFA-Basic 2.0 where PUT was used
just outside the screen. In GFA-Basic 3.0 the program didn't work. The
same occurred in a program where a picture was BLOADed a few bytes before
the screen-RAM. I don't understand why GFA-Basic 3.0 doesn't accept this,
as there is some unused space there (read the paragraph 'RAM'), but I've
learned to correct this when I convert a program from GFA-Basic 2.0 to
version 3.0.
Degas-Pictures
A Degas picture-file contains not only the actual picture (same format as
SGET-picture), but also the colour-palette. Use the Procedure Show.degas
to load and show a Degas-picture :
PROCEDURE show.degas(degas$)
LOCAL degas.picture$,degas.picture%,degas.palette$
LOCAL degas.palette%
degas.picture$=SPACE$(32000)
degas.picture%=VARPTR(degas.picture$)
degas.palette$=SPACE$(32)
degas.palette%=VARPTR(degas.palette$)
OPEN "I",#90,degas$
SEEK #90,2 ! skip resolution
BGET #90,degas.palette%,32 ! load palette of picture
SEEK #90,34
BGET #90,degas.picture%,32000 ! load actual picture
CLOSE #90
~XBIOS(6,L:degas.palette%) ! change palette
SPUT degas.picture$ ! show the picture
RETURN
The original Degas-files have a length of 32034 bytes :
1 word - resolution
16 words - palette
16000 words - picture data
You can use BTST to check the resolution (bit 0 = Low, bit 1 = Medium, bit
2 = High), but you could also look at the extension of the filename
(PI1/PI2/PI3 for Low/Medium/High). In the second Degas-version (Degas
Elite), 16 words for "colour-animation" can be added after the picture
data.
A Degas Elite picture can (and really should!) be saved in a compressed
format. The extensions PC1/PC2/PC3 are used for Low/Medium/High
resolution. Also, the highest bit of the resolution-word is set as a flag
for a compressed picture. With the Procedure Show.comp.degas you can load
and show a compressed Degas-picture.
Neochrome-Pictures
Sometimes the "Neochrome"-format is used for pictures (32128 bytes) :
1 integer - resolution (0/1/2 for Low/Medium/High)
16 words - palette
12 bytes - filename (nnnnnnnn.eee)
4 bytes + 1 word - colour animation data
18 integers - reserved
16000 words - picture data
VSYNC
The VSYNC-command is useful if you want to prevent irritating blinking
during animation. Always VSYNC before drawing a new picture in an
animation sequence (including SPRITE-animation). The program is slowed
down of course, because it waits for a vertical blank interrupt before
starting to draw. But it looks much nicer.
Scroll
With BMOVE and RC_COPY you can accomplish pretty smooth scrolling. GET/PUT
is too slow, and only works on the logical screen. BITBLT is more
flexible, but also more complicated. And not faster (?). Study the
Procedures Scroll.up and Scroll.down to see how you could scroll the
entire screen up or down :
PROCEDURE scroll.up(scroll.lines,scroll.color)
LOCAL n,bytes,move.bytes,source%
IF high.res!
LET bytes=80*scroll.lines
ELSE
LET bytes=160*scroll.lines
ENDIF
move.bytes=32000-bytes
source%=physbase%+bytes
VSYNC
BMOVE source%,physbase%,move.bytes
DEFFILL scroll.color
PBOX 0,scrn.y.max-scroll.lines+1,scrn.x.max,scrn.y.max
FOR n=1 TO DIV(scrn.y.max,scroll.lines)
VSYNC
BMOVE source%,physbase%,move.bytes
NEXT n
RETURN
Can anybody explain the interference lines that sometimes appear during
the scrolling?
Several fade-over effects can be found in the Procedures Up.scroll,
Slider.scroll, etc. Most Procedures can only be used in High resolution.
For scrolling of one or more text-lines you could use the Procedures
Scroll.text.up and Scroll.text.down :
PROCEDURE scroll.text.up(begin,end)
LOCAL screen%,sx,sy,w,h,dx,dy
IF begin>1 AND end>=begin
screen%=XBIOS(3) ! logical screen
sx=0
sy=(begin-1)*char.height
w=scrn.x.max
h=(end-begin+1)*char.height
dx=0
dy=sy-char.height
RC_COPY screen%,sx,sy,w,h TO screen%,dx,dy
ELSE
PRINT bel$;
ENDIF
RETURN
This method is also used in the Procedure Debug.
ACLIP
Line-A commands are 'CLIP-sensitive', so you should always use an appro-
priate ACLIP-command before a Line-A command. The following Line-A
commands are not influenced by ACLIP : ALINE, HLINE, PSET, PTST and
BITBLT.
Blitter
The Blitter-TOS (1987) allows you to switch the Blitter on/off. I couldn't
test the following Procedure, because I don't have a Blitter. But if I
understand the function XBIOS 64 (Blitmode) correctly, you could switch
the Blitter on and off from GFA-Basic :
PROCEDURE blitter(switch!)
LOCAL status
status=XBIOS(64,-1)
IF BTST(status,1) ! Blitter available?
IF switch!
status=BSET(status,0) ! Blitter on
ELSE
status=BCLR(status,0) ! Blitter off
ENDIF
~XBIOS(64,status) ! do it
ENDIF
RETURN
By the way, Line A commands do not use the Blitter. The Blitter-chip
contains a hardware-routine that replaces the Line A BitBlt-function
(BITBLT in GFA-Basic). All Mega ST's have a Blitter installed.
21. EVENTS
MENU()
There is no easy way to test in a program if an accessory has been
selected by the user. MENU(1) looks promising, but the value 40 (select
accessory) or 41 (close accessory) is only sent to the accessory! It
would have been nice to be able to determine if an accessory has been
closed, because GEM will clear the accessory-window and will fill the
space with the desktop-pattern. No easy solution for this one, sorry. The
best idea probably is to test frequently with 'IF MENU(1)=20' if a redraw
is necessary, e.g. :
ON MENU MESSAGE GOSUB redraw
(...)
PROCEDURE redraw
IF MENU(1)=20
(...) ! redraw screen
ENDIF
RETURN
This method only works if you have opened a window.
ON MENU BUTTON
The syntax for ON MENU BUTTON is :
ON MENU BUTTON clicks,button,event GOSUB proc
For both 'button' and 'event' you can use the numbers 0-3. The variable
'clicks' stands for the maximal number of clicks you want to register. If
you choose '2', the Procedure will be called if the user clicks once or
twice :
ON MENU BUTTON 2,1,1 GOSUB proc
It's not possible to wait for a double-click, unless the progam is really
waiting, and not doing anything else :
SELECT EVNT_BUTTON(2,1,1)
CASE 1
' clicked once
CASE 2
' clicked twice
ENDSELECT
But you can't combine this with an ON MENU loop.
If you use the described method :
ON MENU BUTTON 2,1,1 GOSUB proc
you could use MENU(15) in the called Procedure to check if the user
clicked twice. The same Procedure would be called after a single click,
but you could ignore that. Fasten your seatbelts now. If you run such a
program the first time, a double click is not registered in MENU(15). If
you suspect a bug and run the program again, MENU(15) works all right.
Nasty.
Another bug, but this time a GEM-bug (I think), after :
ON MENU BUTTON 1,2,2 GOSUB proc
The Procedure is called immediately, whether the right mouse-button was
pressed or not.
If you want to switch ON MENU BUTTON temporarily off, use :
ON MENU BUTTON clicks,button,event GOSUB dummy
The Procedure Dummy should be empty. Use the same method to switch other
ON MENU commands temporarily off.
ON MENU IBOX
You can define two independent rectangles with ON MENU IBOX and/or ON MENU
OBOX, either :
- one IBOX and one OBOX
- two IBOXes
- two OBOXes
22. PULLDOWN MENU
OPENW 0
Even if you don't need a window, you could 'OPENW 0' if you use a
pulldown menu. The top line (y-coordinates 0 to 18 in High resolution,
that's where your menu is) is now protected against accidental drawing.
After 'OPENW 0' 19 is always added to the y-coordinate, so with 'PLOT 0,0'
the point is actually drawn at (0,19).
Desk-submenu
The first submenu in a pulldown menu should be the Desk-submenu ('Desk'),
usually with the following lay-out :
Info
------------
Accessory 1
Accessory 2
(...)
If the user chooses the Info-item, you should show some information about
the program. The AES takes care of the accessory-items, you simply use
'1,2,3,4,5,6' in the corresponding DATA-line. If you use '-,-,-,-,-,-' the
user won't be able to choose an accessory from your program, but all
loaded accessories still occupy memory.
File-submenu
Most pulldown-menu's contain a File-submenu ('File') as the second
submenu. The following lay-out is more or less standard :
New file ^N
Open file ... ^O
-----------------
Close ^C
Save ^S
Save as ... ^M
Abort ^A
-----------------
Quit ^Q
With '...' you announce that further input from the user will be
requested. If at all possible, you should offer optional keyboard-
alternatives for the experienced user. With '^N' you remind the user of
the <Control> <N> alternative. The character with ASCII-code 7 is used as
the symbol for <Alternate>. By general agreement, the Quit-choice always
is the last item of the File-submenu.
By the way, never leave the last (rightmost) submenu without options, e.g.
during development of a program. There must be at least one option in the
last submenu, or it's reset-time again.
23. WINDOWS
GFA-windows
You'll notice that this chapter is rather short. This reflects my
reservations about the use of windows. I know very few programs that use
windows sensibly. For these programs GEM-windows indeed are a blessing.
But most programs are better off without windows. GEM just slows the
screen-output down. Cover your TOS-screen with windows only if you really
need them.
GFA-Basic has four "easy" windows for not too complicated jobs. Draw an
imaginary cross on the screen. The intersection of the two lines is
determined by the coordinates in 'OPENW n,x,y'. If you show more than one
window on the screen, you should realize that resizing one window auto-
matically changes the size of the other three windows as well (remember
the cross?). Use the AES window-commands if you need independent windows.
The four GFA-windows reserve the top line for a pulldown menu. If you use
your own AES-windows, you should protect the top line yourself, e.g. with
'OPENW 0'.
A standard-method for the use of GFA-windows looks like this :
TITLEW #1," title " ! automatically centered
INFOW #1,"information" ! left justified
OPENW 1 ! or use FULLW #1
CLEARW 1
(...)
CLOSEW 1
CLOSEW 0
I don't know how to use '#' properly. The editor does not accept '#' in
'OPEN #1', you have to use 'OPENW 1'. If you use FULLW instead of OPENW,
you have to use 'FULLW #1'. In this case the '#' has to be used, or the
command doesn't work at all! The GFA-editor always inserts a '#' after
TITLEW and INFOW, even if you didn't type one. What the # is going on?
CLOSEW
If you have opened a window in your program, always use 'CLOSEW 0' before
returning to the editor. If you don't, you can't work normally in Direct
Mode. If you did forget, you can type 'CLOSEW 0' in Direct Mode and
everything works fine again.
If you use the AES window-commands, always call WIND_CLOSE(handle) before
WIND_DELETE(handle).
TITLEW
You can clear a title with :
TITLEW #n," "
Don't use "" instead of " ", or you won't be able to move the window
afterwards!
CLEARW
After 'CLEARW #n' all visible areas of the window are cleared, without
activating the window. GFA uses WIND_UPDATE and WIND_GET for this command.
'CLEARW n' both clears and activates the window.
24. AES-LIBRARY
ALERT
If you need an empty line in an Alert-box, use :
ALERT,3,"...| |...",1,"...",k
Note the space between the two vertical rules.
GFA-Basic allows 4 lines of 30 characters (High/Medium rez) if you use
ALERT, but with FORM_ALERT you can use 5 lines. In both cases you can use
not more than 3 buttons, each at most 8 characters (High/Medium rez) wide:
m$=" line 1 | line 2 | line 3 | line 4 "
ALERT 3,m$,1," OK ",k
'
m$="[3][ line 1 | line 2 | line 3 | line 4 | line 5 ][ OK ]"
k=FORM_ALERT(1,m$)
In Low resolution you can use only half the number of characters you use
in High/Medium resolution.
If some characters are not visible in a button, try broadening the Alert-
box by adding spaces to the widest text-line.
If you have pressed the left mouse-button on the very spot where an Alert-
button is about to appear, something goes wrong. GEM appears to remember
your last mouse-click, and erroneously assumes you pressed the button
after the Alert-box appeared. GEM only selects the button if the mouse-
click occurred in the exact area of the Alert-button. To avoid this
problem you should make sure the user has released the mouse-button before
you call the Alert-box :
REPEAT
UNTIL MOUSEK=0 ! wait until mouse released
By the way, the AES uses an 8K buffer to save the part of the screen where
the Alert-box appears. Ditto for a menu.
SHEL_GET and SHEL_PUT
GFA-Basic is run from a 'Shell-Program', usually the Desktop. In that case
the file DESKTOP.INF can be found in the Environment-Buffer. You can
examine this file by transferring it to a string :
d$=STRING$(1024,0)
r%=SHEL_GET(1024,d$)
PRINTing this string is much easier than using a disk-editor to examine
DESKTOP.INF.
After changing the string, you could put it back in the buffer with :
r%=SHEL_PUT(1024,d$)
This way you could change the lay-out of the desktop. If you change the
string, you should know that the Desktop recognizes the end of DESKTOP.INF
by the byte &H1A. So don't forget to put CHR$(26) at the end. If you want
to save the new buffer, try :
OPEN "O",#1,"\DESKTOP.INF"
PRINT #1,LEFT$(d$,INSTR(d$,CHR$(26)))
CLOSE #1
Is that all there is to tell about the AES-library? Certainly not, so at
this point you will probably be bitterly disappointed. I avoid the AES-
Library whenever I can. Anyway, this is a subject that is thoroughly
covered in most books about GFA-Basic 3.0. Everyone is entitled to his own
opinion about the usefulness of RSC-files and other exotic subjects.
Better still, write this chapter yourself. And the same to you too!
25. GFAXPERT-FILES
Take your time to read this chapter carefully, before using the GFAXPERT-
Procedures or programs. The paragraph 'STANxxxx.LST' is essential reading.
Don't forget to read the 'small print' in the last paragraph.
GFAXPERT.DOC
You're reading it.
GFAXPERT.LIB
This folder (in GFAXPRT2.ARC for downloaders) contains a large collection
of Procedures in several LST-files. Each LST-file contains one or more
folded Procedures. Most Procedures are also listed in this text, or are at
least mentioned. I suggest you Llist all the LST-files with unfolded
Procedures for easy reference. You could also Llist all LST-files again
with folded Procedures, so you will be able to locate a Procedure quickly.
Always read the comment-lines (' ***) in a Procedure before using it. You
will find there if other Procedures are used, or Standard Globals (see
paragraph 'STANxxxx.LST'). Sometimes a Procedure returns a global
variable. Most variables are declared as LOCAL. And do read the relevant
chapter of this text before using a Procedure.
GFA has released a library of graphics- and sound-routines. I am curious
about the quality of these routines, but I certainly do not intend to pay
DM 148.- for the library. My advice would be to use the library
GFAXPERT.LIB and pay DM 148.- to me.
INLINE
The INLINE-folder (in GFAXPRT2.ARC for downloaders) contains INLINE-files
that you will have to load after Merging certain Procedures. This is
clearly indicated in such Procedures.
STANxxxx.LST
Before writing a program, I decide in which resolution the program will
run. Then I Merge the corresponding STANxxx.LST-file (in the folder
GFAXPERT.LIB) and use that as the standard framework for my program :
STANHIGH.LST - High resolution
STANMED.LST - Medium resolution
STANLOW.LST - Low resolution
STANHIME.LST - High or Medium resolution
STANALL.LST - all resolutions
After deleting the first two lines and entering the name of the program
(e.g. TEST.GFA), I Save the program as AAPROG.GFA in the Procedure-
library. In this folder I have a few LST-files ready for easy Merging of
Procedures into the program (AABLOCK1.LST, AABLOCK2.LST, etc.).
Before you continue, you should Llist the five STANxxxx.LST-files. Merge
STANHIGH.LST, unfold all Procedures, then Llist. In the other files you
could restrict unfolding to the Procedure Initio (and perhaps the
Procedures xxx.mode and Standard.xxx.colors). I'll wait until you have the
listings of the STAN-files in front of you...
Right, first I'll discuss STANHIGH.LST thoroughly. Later I'll point out
some important features in the other STAN-files. You will not become an
expert in structured programming immediately, but using the STAN-files
should help.
(1) The first line ('STANHIGH.LST') is there to remind you how to 'Save,A'
this file after you have changed it. Delete it if you are going to develop
a new program.
(2) Enter the name of your new program (e.g. TEST.GFA) and immediately
save the program as AAPROG.GFA in the Procedure-library.
(3) Word-variables are defined as the default.
(4) The Procedure Initio is called. In this Procedure the current
resolution is checked, and after that, a few important global variables
are defined (Standard Globals). The following Standard Globals are defined
(read the comments in the listing for more information) :
high.res! scrn.col.max return$
scrn.lin.max esc$
default.path$ help$
white undo$
physbase% black
logbase% interpreter$
on! run.only$
scrn.x.max off! start.gfa$
scrn.y.max start.prg$
bel$
char.width
char.height
Also, the Standard Array color.index() is created, although you'll only
need it for a few Procedures (e.g. Initio.sprite1).
(5) The Procedure Title.screen is not activated yet. You'll probably want
to change that Procedure.
(6) A normal <Break> should be possible while developing a program. In the
finished program you could activate the Procedure Break, or delete it. Be
very careful with <Break> if the program uses RESERVE or has changed the
address of the physical/logical screen.
(7) The main program usually is not much more than a list of Procedure-
calls, e.g. :
@start.game
@play.game
@score
(8) Try to leave the program by calling the Procedure Exit. During
development it's more convenient to leave with 'EDIT'.
(9) Two Standard Functions are defined: Center$ for centering text and
Rev$ for PRINTing reverse. The last function uses VT52-codes, so use it on
the TOS-screen only.
(10) The Standard Procedure High.mode (called from Initio) checks the
current resolution and aborts with an appropriate message if the program
is run in the wrong resolution.
(11) The Standard Procedure Get.path (called from Initio) returns the
current path. Using the interpreter, GEMDOS returns the path of the
interpreter (e.g. 'A:\'), not of the running program! That's why I use
CHDRIVE + CHDIR in the shell-programs. In that case the Standard Global
default.path$ does contain the path of the running GFA-program. If you
develop the program, you should use CHDRIVE/CHDIR in Direct Mode if
necessary. I am not happy with this method, but I know no other solution
for GFA-programs. If I don't use this method, I always define the path in
the Initiation-part, e.g. :
path$="A:\GAMES\" ! where are the data-files?
Now another GFA-user only has to change the path and the program can find
the necessary files. For owners of the Run-Only interpreter you should
describe the proper configuration in a READ.ME-file. With compiled GFA-
programs there is no problem, because GEMDOS will return the path of the
running program.
(12) I like to begin every program with a title-screen. Change the
Standard Procedure Title.screen, or delete it if you don't like title-
screens in your programs.
(13) The Standard Procedure Return is used by Title.screen.
(14) The Standard Procedure Break can be deleted if you don't need it.
Don't use 'ON BREAK CONT' in your program, unless you have a very good
reason to do so. Note that I have made it possible to use <Break> the
normal way if the user chooses <CONT> while pressing the <Break>-
combination.
(15) The Standard Procedure Exit should always be used. This Procedure
tries to go back to one of the shell-programs. If that's impossible, the
Procedure decides to end with 'EDIT' or 'SYSTEM'. Is it possible to test
in a program if it is run by the interpreter or if it is a compiled
program? If the answer is yes, this Procedure could be improved.
(16) Other Procedures can be inserted after '*** Procedures ***'. Merge
Procedures from the library GFAXPERT.LIB (or your own library) here. I use
the following method :
- New, so memory is clear
- Merge relevant LST-file from library
- choose (folded) Procedure and Write as block to AABLOCKx.LST
- repeat previous steps for other Procedure(s)
- Load AAPROG.GFA
- Merge AABLOCKx.LST at appropriate position(s)
- Save program (press <Return> in Fileselector)
Of course, you'll have to write a few Procedures from scratch. Before you
know it, you'll have reached The End. It's so easy to write a program in
GFA-Basic 3.0, that I actually enjoy programming.
(17) Debugging is perhaps less fun than programming. Merge the Procedure
Debug temporarily in your program if you need some help. And do save your
program regularly! Fall back to the program AAPROG.BAK if your latest
improvements were fatal. If you are completely satisfied, save the program
under its proper name (TEST.GFA). Also save a back-up on another disk,
just in case something terrible should happen to your original program.
(18) In STANALL.LST you'll notice in the Procedure Initio that I always
start with the default palette (Procedures Standard.med.colors and
Standard.low.colors). New Standard Globals in STANALL.LST are :
red med.res!
green low.res!
The Standard Array color.index() is very important in Medium or Low
resolution.
(19) In STANHIME.LST (my favourite) the only important difference is the
Procedure High.med.mode, where the current resolution is checked.
(20) In STANLOW.LST all 16 default-colours are now defined as Standard
Globals :
white blue grey l.purple
black d.blue l.black d.purple
red brown l.blue d.yellow
green d.green bluegreen l.yellow
With two new Standard Functions you can change the colour of PRINTed text
(Ink$) or the colour of the background (Paper$). Both functions work on
the TOS-screen only. The current resolution is checked in the Procedure
Low.mode.
(21) Nothing new in STANMED.LST, except the Procedure Med.mode to check
the resolution.
Perhaps you find all this talk about a "standard" program-structure very
boring. If you write a program, why care about structure? Because it's
much easier to change a structured program, especially if you didn't write
the program yourself. Change an unstructured program, and you're going to
be surprised by some (hidden) unwanted side-effects. Make other users
happy, spread your beautiful programs only if they are structured and well
commented. Do spread the (listable) source of your program, so other users
can learn from you. And don't be insulted if others improve your program.
I certainly will not be insulted if you send me your own thoughts about
structured programming in GFA-Basic 3.0.
START
In the START-folder (in GFAXPRT2.ARC for downloaders) you will find the
following programs :
START.GFA - High or Medium resolution
STARTLOW.GFA - Low resolution
GFASTART.GFA - all resolutions
I use two different disks for my GFA-programs. One for programs that run
in High and/or Medium resolution (save the desktop in Medium resolution!).
And one for programs that run in Low resolution.
START.GFA and STARTLOW.GFA are used as a kind of shell to run the GFA-
programs on these disks. Not a true shell, but it "feels" like all GFA-
programs are run from START.GFA and STARTLOW.GFA. I start a GFA-session
by double-clicking the shell-program on the desktop ('GFA' has been
installed as application for GFABASIC.PRG).
All my programs try to CHAIN one of the shell-programs when the user exits
the program. Two important remarks if you decide to use these shell-
programs :
- vertical frequency is switched to 60 Hz in Medium or Low resolu-
tion; not a good idea if you use a TV through a modulator!
- Write Verify Test is switched off
If you like, you can activate the check for a boot-virus in the shell-
programs. The shell-program and GFABASIC.PRG (or GFABASRO.PRG) should be
in the main directory!
GFASTART.PRG is used in the same way with compiled GFA-programs. All my
compiled GFA-programs try to CHAIN this shell-program when exiting. The
source for this program is GFASTART.GFA. Compile this as GFASTART.PRG and
put it in the main directory.
All three START-programs contain plenty comments, so you should be able to
understand how the programs work by studying the listings. The shell-
programs certainly can be improved, I'm not yet satisfied with the current
programs.
small print
Read this paragraph carefully, before using the GFAXPERT-files. The files
are not Public Domain!
You are free to use and change all GFAXPERT-files, but only for personal
use. Certainly not for commercial use.
You are invited to copy the original GFAXPERT-disk or the GFAXPRTx.ARC-
files and give these to your friends. But you are not allowed to change
anything on the disk or in the files. If you feel the urge to change
something, don't do it, but write to me (read the chapter 'EPILOGUE').
You are not allowed to sell (files from) the GFAXPERT-disk or one of the
GFAXPRTx.ARC-files. The disk GFAXPERT may only be sold by so-called Public
Domain Clubs if they have my written permission to do so. BBS's are free
to make the original GFAXPRTx.ARC-files available for downloading.
Any part from the text GFAXPERT.DOC or the Procedure-library GFAXPERT.LIB
may be quoted in newsletters or magazines, if accompanied by a reference
like :
from GFAXPERT (2nd ed.) by Han Kempen
I cannot be held responsible for any damage that may result from running a
GFAXPERT-program, using a Procedure from the library GFAXPERT.LIB, or
using information from this text.
(c) Han Kempen, 3 July 1990
EPILOGUE
Well, that's all folks. I'm quite certain there are still a few bugs to be
found in this text and in the Procedure-Library GFAXPERT.LIB. By now you
should have become an expert in GFA-Basic 3.0, so you will be able to spot
them bugs immediately. Please let me know if you find one.
Perhaps you still have some unanswered questions about GFA-Basic. Or an
answer to one of my own questions in this text. Or some neat Procedures.
Or a brilliant program. I would appreciate it very much if you would send
your letter and/or disk to :
Han Kempen
Rubensstraat 12
7741 AR Coevorden
the Netherlands
Do share your ideas, Procedures and programs with others, starting with
me. Consider it a small payment for the GFAXPERT-files. Thanks.
Wishing you many happy hours with GFA-Basic 3.0,
Han Kempen
INDEX
(#) = Procedure or Function
\ ..................................... 29
" ..................................... 41
1st Word Plus ......................... 62
40-folder limit ....................... 66
abbreviated commands .................. 12
accessory ............................. 25,101
ACHAR ................................. 96
ACLIP ................................. 99
After$ (#) ............................ 32
AFTER ................................. 82
ALERT ................................. 106
application ........................... 9
ARECT ................................. 88
ASCII-code ............................ 33,34,36,37
Ascii.qsort (#) ....................... 27
ATEXT ................................. 96
attribute ............................. 53
AUTO .................................. 9
BCHG .................................. 30
BCLR .................................. 29
Before$ (#) ........................... 32
Bezier-curve .......................... 92
BGET .................................. 62
BIOS 4 (Rwabs) ....................... 59
BIOS 7 (Getbpb) ...................... 56,60
BIOS 9 (Mediach) ..................... 60
BIOS 10 (Drvmap) ...................... 51
BIOS 11 (Kbshift) ..................... 36
bit-mask .............................. 29
Blitter (#) ........................... 99
BLOAD ................................. 61
Block.dimmer (#) ...................... 91
Boolean ............................... 17
boot .................................. 9,61
BPB ................................... 56
BPUT .................................. 62
break ................................. 10
Break (#) ............................. 110
BSAVE ................................. 61
BSET .................................. 30,79
bug (GFA) ............................. 12,20,24,25,31,32,36,42,80,83,
84,92,96,97,101
bug (TOS) ............................. 25,35,55,56,63,64,66,70,95,102
calculations .......................... 79
Caps (#) .............................. 39
CapsLock .............................. 38
CARD .................................. 31
CHAIN ................................. 83
Change.font (#) ....................... 44
Change.midi.buffer (#) ................ 67
Change.palette (#) .................... 85
characters ............................ 16,34
CHDIR ................................. 51,110
CLEAR ................................. 19
CLEARW ................................ 105
CLIP .................................. 92
CLOSEW ................................ 104
cluster ............................... 58
Coldstart (#) ......................... 9
color ................................. 40
Color.cycle (#) ....................... 86
correlation ........................... 31
COSQ .................................. 30
CURVE ................................. 92
Cut and Paste ......................... 13
Cycle.once (#) ........................ 87
DATE$ ................................. 22
Day.of.week (#) ....................... 22
Debug (#) ............................. 19,111
DEFFILL ............................... 87
DEFLINE ............................... 89
DEFLIST ............................... 15
DEFMARK ............................... 87
DEFMOUSE .............................. 72
DEFTEXT ............................... 89
DEFWRD ................................ 17
Degas-Pictures ........................ 97
Degas.screendump (#) .................. 46
DELAY ................................. 83
Desk-submenu .......................... 103
DESKTOP.INF ........................... 106
DFREE ................................. 56,58
Digital$ (#) .......................... 93
DIM ................................... 19
Dim.colors (#) ........................ 86
DIR$() ................................ 50
DIR ................................... 51
Direct mode ........................... 15
disk format ........................... 55
disk-swap ............................. 60
Do.sound (#) .......................... 76
DRAW .................................. 91
DTA-buffer ............................ 52
DUMP .................................. 19
Editor ................................ 71
EQV ................................... 31
ERASE ................................. 19,24
ERROR ................................. 82
EVERY ................................. 82
EXEC .................................. 24,83
EXIST ................................. 54
Exit (#) .............................. 110
fade-over ............................. 99
Fastprint (#) ......................... 41
FAT ................................... 58
FDC ................................... 55
FGETDTA ............................... 52
File Allocation Table ................. 58
File.copy (#) ......................... 55
File-submenu .......................... 103
FILES ................................. 51
FILESELECT ............................ 63
Fileselect (#) ........................ 64
Fileselector .......................... 63,71
floating point ........................ 18
Floppy Write Test ..................... 50
Folded Procedures ..................... 13
font .................................. 43,89
Font.8x16 (#) ......................... 43
Font.8x8 (#) .......................... 43
FONTKIT ............................... 44
FOR ... NEXT .......................... 79
Force.mediach (#) ..................... 60
FORM_ALERT ............................ 106
FSETDTA ............................... 52
FSFIRST ............................... 52
FSNEXT ................................ 52
Full.fill (#) ......................... 88
FULLW ................................. 104
FUNCTION .............................. 19
GEMDOS 17 (Cprnos) .................... 45
GEMDOS 25 (Dgetdrv) ................... 50
GEMDOS 48 (Sversion) .................. 10
GEMDOS 54 (Dfree) ..................... 56
GEMDOS 67 (Fattrb) .................... 53
GET ................................... 96
Get.path (#) .......................... 110
GFA-windows ........................... 104
GFAXPERT.DOC .......................... 7,108
GFAXPERT.LIB .......................... 7,108
GOSUB ................................. 82
GOTO .................................. 82
GRAPHMODE ............................. 90
HARDCOPY .............................. 45
harddisk .............................. 51,55,58,59
High.screendump.epson (#) ............. 45
High.screendump.star24 (#) ............ 45
HLINE ................................. 96
IBOX .................................. 102
IF ... ENDIF .......................... 78
INFOW ................................. 194
Initio (#) ............................ 109
Initio.fill1 (#) ...................... 88
Initio.high.fill1 (#) ................. 87
Initio.keyget (#) ..................... 36
Initio.logical.screen (#) ............. 42
Initio.mouse1 (#) ..................... 72
Initio.printer (#) .................... 47
Initio.sound (#) ...................... 75
Initio.sprite1 (#) .................... 93
Initio.text.array (#) ................. 62
Ink$ (#) .............................. 40
INKEY$ ................................ 33
INLINE ................................ 24,108
INP ................................... 62,67,70
INPAUX$ ............................... 70
INPMID$ ............................... 67
INPUT ................................. 34,62
INPUT$ ................................ 35
Insert-mode ........................... 15
INSTR ................................. 32
INT{} ................................. 23
integer ............................... 18
Intel.word (#) ........................ 31
interleave ............................ 57
Invert.block (#) ...................... 91
joystick .............................. 74
Key.click (#) ......................... 38
Key.repeat (#) ........................ 39
keyboard .............................. 37
keyboard-buffer ....................... 33
Keyboard.version (#) .................. 37
KEYDEF ................................ 37
KEYGET ................................ 35
KEYLOOK ............................... 36
KEYPAD ................................ 37
KEYPRESS .............................. 37
KEYTEST ............................... 35
KILL .................................. 54
Line-A ................................ 95
LINE INPUT ............................ 35,62
Llist ................................. 14
Load .................................. 13
LOCATE ................................ 41
LOF ................................... 54
LOG ................................... 30
logical screen ........................ 42
loops ................................. 80,119
LSET .................................. 32
Make.high.fill (#) .................... 88
Make.mouse (#) ........................ 73
Make.palette.string (#) ............... 85
Make.sprite (#) ....................... 94
MALLOC ................................ 25
MAX ................................... 31,78
Max.array (#) ......................... 31
memory ................................ 23,24,25
MENU() ................................ 101
Midi-buffer ........................... 67
Midi-commands ......................... 67
Midi.monitor (#) ...................... 68
MIN ................................... 78
MOD ................................... 29
monitor ............................... 10
MOUSE ................................. 71
MS-DOS disk ........................... 58
NAME .................................. 54
Neochrome-Pictures .................... 98
New.med.colors (#) .................... 85
Normal.font (#) ....................... 44
NOT ................................... 81
OBOX .................................. 102
ON BREAK GOSUB ........................ 82
ON MENU BUTTON ........................ 101
ON MENU IBOX .......................... 102
OPENW ................................. 104
OPENW 0 ............................... 103
Operating System ...................... 10
OUT ................................... 40,62
Overwrite-mode ........................ 15
palette ............................... 84,111
Palette.box (#) ....................... 86
Paper$ (#) ............................ 40
Parse.filename (#) .................... 65
parser ................................ 32
PCIRCLE ............................... 92
physical screen ....................... 42
Play.midi (#) ......................... 68
PLOT .................................. 91
point-commands ........................ 14
PRED .................................. 29
PRINT ................................. 40
Print.stopwatch (#) ................... 21
PRINT TAB ............................. 42
printer-commands ...................... 47
printer-driver ........................ 14
printer-parameters .................... 45
Printer.ready (#) ..................... 45
PUT ................................... 96
QSORT ................................. 26
RAM ................................... 23
RAM-disk .............................. 50
READ .................................. 20
RECALL ................................ 62
Record.midi (#) ....................... 67
RESERVE ............................... 23
reset ................................. 9,50
RESTORE ............................... 20
Restore.palette (#) ................... 85
Restore.physical.screen (#) ........... 43
Return (#) ............................ 110
reverse ............................... 41,84
Rgb.value (#) ......................... 85
right justification ................... 32
RS232-buffer .......................... 70
RSET .................................. 32
Rubber.line (#) ....................... 90
samples ............................... 76
Save .................................. 14
Save,A ................................ 14
Save.palette (#) ...................... 85
scan-code ............................. 33,36,37,38
scrap-library ......................... 83
Screen.dimmer (#) ..................... 91
Screendump (#) ........................ 45
Scroll.text.up (#) .................... 99
Scroll.up (#) ......................... 98
sectors ............................... 59
SELECT ................................ 78
serial number ......................... 57
SETCOLOR .............................. 84
SETMOUSE .............................. 71
SHEL_GET .............................. 106
SHEL_PUT .............................. 106
shell-program ......................... 112
SHL ................................... 79
Show.degas (#) ........................ 97
Show.text.page (#) .................... 63
Shuffle (#) ........................... 20
SINQ .................................. 30
SOUND ................................. 75
Soundmachine .......................... 77
special characters .................... 16
speech ................................ 77
SPRITE ................................ 93
SSORT ................................. 26
Standard .............................. 7
Standard Array ........................ 109,111
Standard Functions .................... 110,111
Standard Globals ...................... 109,111
STANxxxx.LST .......................... 108
START ................................. 112
start-up .............................. 9
Step Rate ............................. 50
STICK ................................. 74
Stopwatch (#) ......................... 21
STORE ................................. 62
STRIG ................................. 74
String.index.qsort (#) ................ 28
SUCC .................................. 29
supervisor mode ....................... 23
SWAP .................................. 20,31
Swap.screen (#) ....................... 43
syntax ................................ 12
system-font ........................... 43,89
Tab ................................... 13,41
TAB ................................... 42
TEXT .................................. 92
text-array ............................ 62
Text.at (#) ........................... 92
TIME$ ................................. 21
Time (#) .............................. 21
TIMER ................................. 21
Title.screen (#) ...................... 110
TITLEW ................................ 104,105
TOS ................................... 10
TOS-screen ............................ 40
TOUCH ................................. 54
twisted format ........................ 57
TYPE .................................. 20
VAR ................................... 18
variable type ......................... 17
VDISYS 38 (vqt_attributes) ............ 89
VQT_EXTENT ............................ 95
VSETCOLOR ............................. 84
VSYNC ................................. 98
VT52 .................................. 40
Warmstart (#) ......................... 9
WAVE .................................. 75
WIND_CLOSE ............................ 104
word .................................. 17,31
WORD .................................. 23,59
write-protect ......................... 60
XBIOS 0 (Initmous) ................... 71
XBIOS 5 (Setscreen) .................. 42
XBIOS 6 (Setpalette) ................. 85,97
XBIOS 7 (Setcolor) ................... 84
XBIOS 8 (Floprd) ..................... 60
XBIOS 10 (Flopfmt) .................... 56
XBIOS 15 (Rsconf) ..................... 70
XBIOS 16 (Keytbl) ..................... 37,38
XBIOS 18 (Protobt) .................... 57
XBIOS 19 (Flopver) .................... 56
XBIOS 32 (Dosound) .................... 75
XBIOS 33 (Setprt) ..................... 45
XBIOS 36 (Prtblk) ..................... 47
XBIOS 64 (Blitmode) ................... 99